Saltar a contenido

Series

Podríamos pensar en una serie como un ndarray en el que cada valor tiene asignado una etiqueta (índice) y además admite un título (nombre).

Creación

Veamos varios mecanismos de creación de series, con el objetivo de conseguir los valores \([1,2,3]\):

>>> pd.Series([1, 2, 3])
0    1
1    2
2    3
dtype: int64

Índice por defecto

El índice por defecto (etiquetas de los datos) empieza en 0 y usa números enteros positivos.

>>> pd.Series(range(1, 4), index=['a', 'b', 'c'])
a    1
b    2
c    3
dtype: int64
>>> items = {'a': 1, 'b': 2, 'c': 3}

>>> pd.Series(items)
a    1
b    2
c    3
dtype: int64

Ninguna de las series anteriores tienen nombre. Lo podemos hacer usando el parámetro name en la creación de la serie:

>>> pd.Series([1, 2, 3], name='integers')
0    1
1    2
2    3
Name: integers, dtype: int64

Ejercicio

Crea una serie de pandas con valores enteros en el intervalo \([1,26]\) y etiquetas 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. Busca una manera programática (no manual) de hacerlo (recuerda el módulo string).

Solución

Atributos

Las series en pandas contienen gran cantidad de atributos. A continuación destacaremos algunos de ellos.

Para ilustrar los diferentes ejemplos de todo este capítulo vamos a usar datos de gases nobles1:

  • Construcción de la serie:

    >>> density = pd.Series({#(1)!
    ...     'Helio': 0.1785,
    ...     'Neón': 0.9002,
    ...     'Argón': 1.7818,
    ...     'Kriptón': 3.708,
    ...     'Xenón': 5.851,
    ...     'Radón': 9.97
    ... }, name='Gas Density')
    

    1. Densidad de los gases nobles medida en \(kg/m^3\)
  • Visualización de la serie:

    >>> density
    Helio      0.1785
    Neón       0.9002
    Argón      1.7818
    Kriptón    3.7080
    Xenón      5.8510
    Radón      9.9700
    Name: Gas Density, dtype: float64
    
>>> density.index
Index(['Helio', 'Neón', 'Argón', 'Kriptón', 'Xenón', 'Radón'], dtype='object')
>>> density.values
array([0.1785, 0.9002, 1.7818, 3.708 , 5.851 , 9.97  ])
>>> density.dtype
dtype('float64')
>>> density.name
'Gas Density'    
>>> employees.nbytes
48
>>> employees.size
6 

Selección de registros

La selección de los datos se puede realizar desde múltiples aproximaciones. A continuación veremos las posiblidades que nos ofrece pandas para seleccionar/filtrar los registros de una serie.

Selección usando indexado numérico

Para acceder a los registros por su posición (índice numérico) utilizamos el atributo .iloc:

>>> density.iloc[0]#(1)!
np.float64(0.1785)

>>> density.iloc[-1]
np.float64(9.97)

>>> density.iloc[2:5]
Argón      1.7818
Kriptón    3.7080
Xenón      5.8510
Name: Gas Density, dtype: float64

>>> density.iloc[1:6:2]
Neón       0.9002
Kriptón    3.7080
Radón      9.9700
Name: Gas Density, dtype: float64

  1. El uso density[0] está obsoleto y se recomienda la migración a density.iloc[0]. En caso de usarlo obtendríamos el siguiente warning: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use ser.iloc[pos].

Selección usando etiquetas

En el caso de aquellas series que dispongan de un índice con etiquetas, podemos acceder a sus registros utilizando el atributo loc:

>>> density.loc['Helio']#(1)!
np.float64(0.1785)

>>> density['Helio':'Xenón']
Helio      0.1785
Neón       0.9002
Argón      1.7818
Kriptón    3.7080
Xenón      5.8510
Name: Gas Density, dtype: float64

>>> density['Helio':'Xenón':2]
Helio    0.1785
Argón    1.7818
Xenón    5.8510
Name: Gas Density, dtype: float64

  1. Equivale a la sintaxis density['Helio'] o incluso a density.Helio.

Fragmentos de comienzo y fin

A nivel exploratorio, es bastante cómodo acceder a una porción inicial (o final) de los datos que manejamos. Esto se puede hacer de forma muy sencilla con series:

>>> density.head(3)
Helio    0.1785
Neón     0.9002
Argón    1.7818
Name: Gas Density, dtype: float64

>>> density.tail(3)
Kriptón    3.708
Xenón      5.851
Radón      9.970
Name: Gas Density, dtype: float64

Operaciones

Si tenemos en cuenta que una serie contiene valores en formato ndarray podemos concluir que las operaciones sobre arrays son aplicables al caso de las series.

Veamos algunos ejemplos de operaciones que podemos aplicar sobre series:

Operaciones lógicas

Supongamos que queremos filtrar aquellos gases cuya densidad sea mayor que 3 \(kg/m^3\):

>>> density
Helio      0.1785
Neón       0.9002
Argón      1.7818
Kriptón    3.7080
Xenón      5.8510
Radón      9.9700
Name: Gas Density, dtype: float64

>>> density > 3#(1)!
Helio      False
Neón       False
Argón      False
Kriptón     True
Xenón       True
Radón       True
Name: Gas Density, dtype: bool

>>> density[density > 3]#(2)!
Kriptón    3.708
Xenón      5.851
Radón      9.970
Name: Gas Density, dtype: float64

  1. Se obtiene una serie «booleana».
  2. Para obtener los datos filtrados debemos aplicar la «máscara» anterior.

Ordenación

Es posible ordenar una serie por sus valores o por su índice:

>>> density.sort_values()#(1)!
Helio      0.1785
Neón       0.9002
Argón      1.7818
Kriptón    3.7080
Xenón      5.8510
Radón      9.9700
Name: Gas Density, dtype: float64

    • Ordenación de una serie por sus valores.
    • El argumento ascending=False hará que la ordenación sea de mayor a menor.
    • El argumento inplace=True hará que se modifique la serie en vez de retornar una nueva.

>>> density.sort_index()#(1)!
Argón      1.7818
Helio      0.1785
Kriptón    3.7080
Neón       0.9002
Radón      9.9700
Xenón      5.8510
Name: Gas Density, dtype: float64

    • Ordenación de una serie por su índice.
    • El argumento ascending=False hará que la ordenación sea de mayor a menor.
    • El argumento inplace=True hará que se modifique la serie en vez de retornar una nueva.

Contando valores

>>> marks = pd.Series([5, 5, 3, 6, 5, 2, 8, 3, 8, 7, 6])

>>> marks.value_counts()#(1)!
5    3
3    2
6    2
8    2
2    1
7    1
dtype: int64

>>> marks.nunique()#(2)!
6

>>> marks.count()#(3)!
11

  1. Tabla de frecuencias de la serie.
  2. Número de valores únicos de la serie.
  3. Número de valores no nulos de la serie.

Operaciones aritméticas

Se distingue el tipo de operación en función de los operandos:

Operaciones entre series y escalaras

En el siguiente ejemplo pasamos la densidad de los gases nobles de \(kg/m^3\) a \(g/m^3\):

>>> density * 1000
Helio       178.5
Neón        900.2
Argón      1781.8
Kriptón    3708.0
Xenón      5851.0
Radón      9970.0
Name: Gas Density, dtype: float64

Operaciones entre series

Para trabajar a posteriori, vamos a crear una nueva serie que almacena el radio atómico de los gases nobles medido en \(nm\)1:

>>> radius = pd.Series({
...     'Helio': 0.05,
...     'Neón': 0.07,
...     'Argón': 0.094,
...     'Kriptón': 0.109,
...     'Xenón': 0.13,
...     'Radón': 0.152
... }, name='Gas Radius')

Supongamos que en el siguiente ejemplo queremos calcular la proporción entre el radio y la densidad de cada gas noble:

>>> radius / density
Helio      0.280112
Neón       0.077760
Argón      0.052756
Kriptón    0.029396
Xenón      0.022218
Radón      0.015246
dtype: float64

Correspondencia de operación

Ten en cuenta que las operaciones se realizan entre registros que tienen el mismo índice (etiqueta).

Funciones estadísticas

Existen multitud de funciones estadísticas que podemos aplicar a una serie. Dependiendo del tipo de dato con el que estamos trabajando, serán más útiles unas que otras.

Veamos dos ejemplos de función estadística:

>>> density.mean()
np.float64(3.731583333333333)

>>> radius.std()
np.float64(0.03772753194507516)

Máximos y mínimos

El abanico de posibilidades es muy amplio en cuanto a la búsqueda de valores máximos y mínimos en una serie. Veamos lo que nos ofrece pandas a este respecto:

>>> density.min()#(1)!
np.float64(0.1785)

>>> density.max()#(2)!
np.float64(9.97)

  1. Valor mínimo de la serie.
  2. Valor máximo de la serie.

>>> density.argmin()#(1)!
np.int64(0)

>>> density.argmax()#(2)!
np.int64(5)

  1. Índice del valor mínimo de la serie.
  2. Índice del valor máximo de la serie.

>>> density.idxmin()#(1)!
'Helio'
>>> density.idxmax()#(2)!
'Radón'

  1. Etiqueta del valor mínimo de la serie.
  2. Etiqueta del valor máximo de la serie.

>>> density.nsmallest(3)#(1)!
Helio    0.1785
Neón     0.9002
Argón    1.7818
Name: Gas Density, dtype: float64

>>> density.nlargest(3)#(2)!
Radón      9.970
Xenón      5.851
Kriptón    3.708
Name: Gas Density, dtype: float64

  1. \(n\) menores valores de la serie.
  2. \(n\) mayores valores de la serie.

Exportación de series

Suele ser bastante habitual intercambiar datos en distintos formatos (y aplicaciones). Para ello, pandas nos permite exportar una serie a multitud de formatos. Veamos algunos de ellos:

>>> density.to_list()
[0.1785, 0.9002, 1.7818, 3.708, 5.851, 9.97]
>>> density.to_dict()
{'Helio': 0.1785, 'Neón': 0.9002, 'Argón': 1.7818, 'Kriptón': 3.708, 'Xenón': 5.851, 'Radón': 9.97}
>>> density.to_csv()
',Gas Density\nHelio,0.1785\nNeón,0.9002\nArgón,1.7818\nKriptón,3.708\nXenón,5.851\nRadón,9.97\n'
>>> density.to_json()
'{"Helio":0.1785,"Ne\\u00f3n":0.9002,"Arg\\u00f3n":1.7818,"Kript\\u00f3n":3.708,"Xen\\u00f3n":5.851,"Rad\\u00f3n":9.97}'
>>> density.to_frame()
         Gas Density
Helio         0.1785
Neón          0.9002
Argón         1.7818
Kriptón       3.7080
Xenón         5.8510
Radón         9.9700

Y muchos otros como: to_clipboard(), to_numpy(), to_pickle(), to_string(), to_xarray(), to_excel(), to_hdf(), to_latex(), to_markdown(), to_period(), to_sql() o to_timestamp().


  1. Datos capturados desde Wikipedia en abril de 2025.