Gestionando la ausencia de datos

Ocurre con frecuencia que disponemos de catálogos de datos donde hay muestras incompletas. Por ejemplo, los datos obtenidos a partir de encuestas donde se registran preguntas sin responder o sensores que no proporcionan ningún valor viable, etc.

Hay que aceptarlo y saber gestionarlo

Pandas asigna el valor o el código NaN (Not a Number) a los valores desconocidos. Más especificamente, los objetos son designados como: None y las fechas como NaT.

Las operaciones que involucren este tipo de datos internamente han de manejar los correspondientes códigos: NaN, None o NaT. ¿Cómo afecta un NaN a una media aritmética?

En este capítulo trabajaremos con esta típología de valores.

[1]:
# Y finalmente,  podemos asignar y usar nans
import numpy as np
datos = np.array([1,2,np.nan,4,5,6,np.nan,8])
print(datos)

print(datos.mean())

[ 1.  2. nan  4.  5.  6. nan  8.]
nan
[2]:
import pandas as pd
[8]:
#Empezamos cargando datos: who.csv con 358 columnas!
df = pd.read_csv("data/who.csv")
df.isna().sum()

# df = df[["Country",df.columns[-2]]]
# print(df[:5])
[8]:
Country                            0
CountryID                          0
Continent                          0
Adolescent fertility rate (%)     25
Adult literacy rate (%)           71
                                  ..
Under_five_mortality_from_IHME    32
Under_five_mortality_rate         21
Urban_population                  14
Urban_population_growth           14
Urban_population_pct_of_total     14
Length: 358, dtype: int64
[66]:
# Como ya sabéis através de la API se puede obtener una descripción más detallada de las posibilidades de cada método de Python, y en especial
# de los métodos de Pandas.
# Para cargar un fichero de tamaño elevado es recomendable cargar aquellos atributos que nos interesen desde un principio usando el argumento: usecols
# https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_csv.html
df = pd.read_csv("data/who.csv", usecols=["Country","Urban_population_growth"])
print(df[:5])
       Country  Urban_population_growth
0  Afghanistan                     5.44
1      Albania                     2.21
2      Algeria                     2.61
3      Andorra                      NaN
4       Angola                     4.14
[71]:
# ¿Qué valor corresponde a un NA del dataframe?
# https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.isna.html
df.isna().sum()
[71]:
Country                     0
Urban_population_growth    14
dtype: int64
[ ]:
#¿Qué columnas tienen datos sin valor: NaN, NaT, None?
# https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.any.html

print(df.columns[df.isna().any()])

# Equivale a preguntar si ¿existe algún valor positivo dentro de esas series?
print("-"*30)
print(df..any())isna()
Index(['Urban_population_growth'], dtype='object')
------------------------------
Country                    False
Urban_population_growth     True
dtype: bool
[31]:
#No dudéis en ejecutar "partes" (dividamos la instrucción para comprenderla)
print(df.isna()[:5])
   Country  Urban_population_growth
0    False                    False
1    False                    False
2    False                    False
3    False                     True
4    False                    False
[11]:
#¿Cuántas muestras son correctas?
df.notna().sum()
# https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.notna.html
# y de cuantas muestras?

[11]:
Country                    202
Urban_population_growth    188
dtype: int64
[13]:
df.notnull().sum() #ambas funcionas son equivalentes en Pandas, no en numpy
[13]:
Country                    202
Urban_population_growth    188
dtype: int64

Tratando la ausencia de datos

  • Ignorando: “Hay X muestras válidas de tantas”

  • Rellenando: reemplazar muestras desconocidas por otros valores: media, valor neutro, etc.

[39]:
#La manera más optima de remplazar estos valores es con la función: fillna
print(df.fillna(0)[:5])

       Country  Urban_population_growth
0  Afghanistan                     5.44
1      Albania                     2.21
2      Algeria                     2.61
3      Andorra                     0.00
4       Angola                     4.14
[14]:
# Si queremos que nuestra variable de dataframe contenga dichas asignaciones recordad asignar la operación a la variable pertinente o a una nueva
df = df.fillna(0)

Maneras de rellenar una serie con datos NA

Cuando los dataframes contienen números la operabildad con valores perdidos puede gestionarse de manera más eficiente. Pongamos un ejemplo:

[5]:
import numpy as np

np.random.seed(20)

#Creamos un dataframe
df = pd.DataFrame(np.random.randn(5, 3),
                     index=['a', 'b', 'c', 'd', 'e'],
                     columns=['one', 'two', 'three'])
print(df)
        one       two     three
a  0.883893  0.195865  0.357537
b -2.343262 -1.084833  0.559696
c  0.939469 -0.978481  0.503097
d  0.406414  0.323461 -0.493411
e -0.792017 -0.842368 -1.279503
[7]:
#Creamos valores NaN para testear
df.two[df.two<0]=np.nan
print(df)
        one       two     three
a  0.883893  0.195865  0.357537
b -2.343262       NaN  0.559696
c  0.939469       NaN  0.503097
d  0.406414  0.323461 -0.493411
e -0.792017       NaN -1.279503
/var/folders/6j/7gfvt_29797dypw8t1wttblw0000gn/T/ipykernel_10061/13172693.py:2: FutureWarning: ChainedAssignmentError: behaviour will change in pandas 3.0!
You are setting values through chained assignment. Currently this works in certain cases, but when using Copy-on-Write (which will become the default behaviour in pandas 3.0) this will never work to update the original DataFrame or Series, because the intermediate object on which we are setting values will behave as a copy.
A typical example is when you are setting values in a column of a DataFrame, like:

df["col"][row_indexer] = value

Use `df.loc[row_indexer, "col"] = values` instead, to perform the assignment in a single step and ensure this keeps updating the original `df`.

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy

  df.two[df.two<0]=np.nan

Podemos usar fillna para rellenar de diversas maneras la serie o series. Por ejemplo, usando una operación de agregación como la media

[21]:
print(df)
print("-"*33)
print(df.fillna(df.mean()))
        one       two     three
a  0.883893  0.195865  0.357537
b -2.343262       NaN  0.559696
c  0.939469       NaN  0.503097
d  0.406414  0.323461 -0.493411
e -0.792017       NaN -1.279503
---------------------------------
        one       two     three
a  0.883893  0.195865  0.357537
b -2.343262  0.259663  0.559696
c  0.939469  0.259663  0.503097
d  0.406414  0.323461 -0.493411
e -0.792017  0.259663 -1.279503
[30]:
#Con un valor en concreto del propio dataframe
print(df.fillna("HOLA"))
print("-"*33)
print(df.fillna(df.loc["a", ["one"]].values[0]))
        one       two     three
a  0.883893  0.195865  0.357537
b -2.343262      HOLA  0.559696
c  0.939469      HOLA  0.503097
d  0.406414  0.323461 -0.493411
e -0.792017      HOLA -1.279503
---------------------------------
        one       two     three
a  0.883893  0.195865  0.357537
b -2.343262  0.883893  0.559696
c  0.939469  0.883893  0.503097
d  0.406414  0.323461 -0.493411
e -0.792017  0.883893 -1.279503

Podemos rellenar con datos interpolados

En la documentación vemos una serie de ejemplos: Interpolate

[8]:
print(df)
print("-"*35)
print(df.interpolate())
        one       two     three
a  0.883893  0.195865  0.357537
b -2.343262       NaN  0.559696
c  0.939469       NaN  0.503097
d  0.406414  0.323461 -0.493411
e -0.792017       NaN -1.279503
-----------------------------------
        one       two     three
a  0.883893  0.195865  0.357537
b -2.343262  0.238397  0.559696
c  0.939469  0.280929  0.503097
d  0.406414  0.323461 -0.493411
e -0.792017  0.323461 -1.279503
[53]:
print(df.interpolate(axis=1)) # Tomemos como referencia el valor NA de (b,"two")
print("--"*35)
print(df.mean(axis=1).b)
        one       two     three
a  0.883893  0.195865  0.357537
b -2.343262 -0.891783  0.559696
c  0.939469  0.721283  0.503097
d  0.406414  0.323461 -0.493411
e -0.792017 -1.035760 -1.279503
----------------------------------------------------------------------
-0.8917828081181468
[62]:
# Para usar otro tipo de interpolaciones es recomendable tener un índice numérico por cuestiones de frecuencia en el método de interpolación
df.index = range(len(df))
print(df.two.interpolate(method="pad"))
0    0.195865
1    0.195865
2    0.195865
3    0.323461
4    0.323461
Name: two, dtype: float64
[63]:
print(df.two.interpolate(method="nearest"))
0    0.195865
1    0.195865
2    0.323461
3    0.323461
4         NaN
Name: two, dtype: float64
[64]:
print("Valores interpolados:" + str(df.two.interpolate().count()-df.two.count()))
Valores interpolados:3

Eliminación de valores NA

Existen operaciones para la eliminación de valores NA

[31]:
print(df)
print("-"*35)
print(df.dropna())

# https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.dropna.html
        one       two     three
a  0.883893  0.195865  0.357537
b -2.343262       NaN  0.559696
c  0.939469       NaN  0.503097
d  0.406414  0.323461 -0.493411
e -0.792017       NaN -1.279503
-----------------------------------
        one       two     three
a  0.883893  0.195865  0.357537
d  0.406414  0.323461 -0.493411
[32]:
#O bien, podemos borrar cambiando el eje AXIS=0 o 1
df.dropna(axis=1)
[32]:
one three
a 0.883893 0.357537
b -2.343262 0.559696
c 0.939469 0.503097
d 0.406414 -0.493411
e -0.792017 -1.279503
[36]:
# el argumento AXIS está en un gran número de métodos de Pandas
print(df.mean()) # y por defecto, suele ser axis=0 (considerar las columnas ejeX)
print("-"*35)
print(df.mean(axis=1))
one     -0.181100
two      0.259663
three   -0.070517
dtype: float64
-----------------------------------
a    0.479098
b   -0.891783
c    0.721283
d    0.078822
e   -1.035760
dtype: float64

Ejercicios

1) Del fichero who.csv, contabiliza cuántos paises tienen algun valor NaN.

[ ]:
df = pd.read_csv("data/who.csv")
df.isna()

1b) Ordena el anterior resultado para identificar cuál es el pais con mayor número de campos desconocidos.

[ ]:

2) who.csv, Selecciona la primera, tercera y decima columna, de las filas comprendidas entre la 100 y la 150.

[ ]:

2b) ¿Cuántos valores NaN hay presentes?

[ ]:

2c) Crea un nuevo dataframe donde los NaN sean cero.

[ ]:

2d) Elimina aquellas filas de la anterior selección donde haya NaN.

[ ]:

Series Temporales

Las series temporales son muestras de valores tomadas a lo largo del tiempo con un muestreo generalmente equidistante. Por ejemplo, información económica, demográfica, meteorológica; registros de seguridad, actividad, etc.

La biblioteca Pandas gestiona las series temporales utilizando el índice: una fecha (datetime): https://docs.python.org/es/3/library/datetime.html

El índice de un dataframe es el pilar básico de acceso a los valores, por lo que su uso simplifica procesos de filtrado, selección, interpolación, etc.

Enlace a la documentación: TimeSeries

[9]:
import pandas as pd
df = pd.read_csv("data/rdu-weather-history.csv",sep=";")
#Qué contiene el fichero
print(df.head())
         date  temperaturemin  temperaturemax  precipitation  snowfall  \
0  2015-04-08            62.1            84.0           0.00       0.0
1  2015-04-20            63.0            78.1           0.28       0.0
2  2015-04-26            45.0            54.0           0.02       0.0
3  2015-04-28            39.0            69.1           0.00       0.0
4  2015-05-03            46.9            79.0           0.00       0.0

   snowdepth  avgwindspeed  fastest2minwinddir  fastest2minwindspeed  \
0        0.0          5.82                40.0                 29.97
1        0.0         11.86               180.0                 21.92
2        0.0          5.82                50.0                 12.97
3        0.0          2.68                40.0                 12.08
4        0.0          2.68               200.0                 12.08

   fastest5secwinddir  ...  drizzle snow freezingrain smokehaze thunder  \
0                30.0  ...       No   No           No       Yes      No
1               170.0  ...       No   No           No        No     Yes
2                40.0  ...       No   No           No        No      No
3                40.0  ...       No   No           No        No      No
4               210.0  ...       No   No           No        No      No

  highwind hail blowingsnow dust freezingfog
0       No   No          No   No          No
1       No   No          No   No          No
2       No   No          No   No          No
3       No   No          No   No          No
4       No   No          No   No          No

[5 rows x 28 columns]
[10]:
print(df.date.sort_values())
2509    2007-01-01
1065    2007-01-02
1066    2007-01-03
1067    2007-01-04
3251    2007-01-05
           ...
2507    2019-06-19
2508    2019-06-20
488     2019-06-21
489     2019-06-22
3623    2019-06-23
Name: date, Length: 4557, dtype: object

Nosotros solo cubriremos los aspectos básicos de estos tipos de datos; lo que queremos es poder responder preguntas similares a las siguientes: - ¿Cómo podría obtener la temperatura media de un año? - ¿Cómo podría obtener la temperatura más alta de todos los meses de julio?

En primer lugar, se ha de transformar el índice en una Fecha:

[16]:
from pandas import DatetimeIndex

import pandas as pd
df = pd.read_csv("data/rdu-weather-history.csv",sep=";")

df.index = DatetimeIndex(df["date"])
df.sort_index(inplace=True)
df.head()


[16]:
date temperaturemin temperaturemax precipitation snowfall snowdepth avgwindspeed fastest2minwinddir fastest2minwindspeed fastest5secwinddir ... drizzle snow freezingrain smokehaze thunder highwind hail blowingsnow dust freezingfog
date
2007-01-01 2007-01-01 48.9 68.0 0.45 0.0 0.0 12.75 190.0 25.05 180.0 ... No No No No No No No No No No
2007-01-02 2007-01-02 32.0 55.9 0.00 0.0 0.0 3.13 320.0 12.97 330.0 ... No No No No No No No No No No
2007-01-03 2007-01-03 28.9 62.1 0.00 0.0 0.0 2.24 220.0 14.09 220.0 ... No No No No No No No No No No
2007-01-04 2007-01-04 46.0 69.1 0.00 0.0 0.0 4.47 220.0 14.09 230.0 ... No No No No No No No No No No
2007-01-05 2007-01-05 57.0 72.0 0.86 0.0 0.0 8.05 190.0 21.03 190.0 ... No No No No No No No No No No

5 rows × 28 columns

[12]:
df = df.drop(columns="date")
[17]:
df.index.day
[17]:
Index([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10,
       ...
       14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
      dtype='int32', name='date', length=4557)
[18]:
df.loc["2014"]
[18]:
date temperaturemin temperaturemax precipitation snowfall snowdepth avgwindspeed fastest2minwinddir fastest2minwindspeed fastest5secwinddir ... drizzle snow freezingrain smokehaze thunder highwind hail blowingsnow dust freezingfog
date
2014-01-01 2014-01-01 29.1 51.1 0.00 0.0 0.0 2.46 200.0 8.95 210.0 ... No No No No No No No No No No
2014-01-02 2014-01-02 37.0 48.9 0.33 0.0 0.0 2.68 310.0 12.97 320.0 ... No No No No No No No No No No
2014-01-03 2014-01-03 22.1 43.0 0.00 0.0 0.0 7.38 300.0 21.92 310.0 ... No No No No No No No No No No
2014-01-04 2014-01-04 19.2 37.9 0.00 0.0 0.0 2.24 120.0 8.05 120.0 ... No No No No No No No No No No
2014-01-05 2014-01-05 37.0 62.1 0.00 0.0 0.0 4.92 180.0 16.11 180.0 ... No No No No No No No No No No
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
2014-12-27 2014-12-27 31.1 62.1 0.00 0.0 0.0 3.13 230.0 12.08 240.0 ... No No No Yes No No No No No No
2014-12-28 2014-12-28 48.0 61.0 0.01 0.0 0.0 6.71 230.0 14.99 230.0 ... No No No No No No No No No No
2014-12-29 2014-12-29 41.0 55.0 0.72 0.0 0.0 6.71 30.0 12.08 50.0 ... No No No No No No No No No No
2014-12-30 2014-12-30 28.2 41.0 0.02 0.0 0.0 5.82 40.0 17.00 40.0 ... No No No No No No No No No No
2014-12-31 2014-12-31 26.2 46.0 0.00 0.0 0.0 1.34 50.0 8.95 270.0 ... No No No No No No No No No No

365 rows × 28 columns

[19]:
df.loc["2014-01-03"]
[19]:
date                    2014-01-03
temperaturemin                22.1
temperaturemax                43.0
precipitation                  0.0
snowfall                       0.0
snowdepth                      0.0
avgwindspeed                  7.38
fastest2minwinddir           300.0
fastest2minwindspeed         21.92
fastest5secwinddir           310.0
fastest5secwindspeed         31.99
fog                             No
fogheavy                        No
mist                            No
rain                            No
fogground                       No
ice                             No
glaze                           No
drizzle                         No
snow                            No
freezingrain                    No
smokehaze                       No
thunder                         No
highwind                        No
hail                            No
blowingsnow                     No
dust                            No
freezingfog                     No
Name: 2014-01-03 00:00:00, dtype: object
[20]:
df.loc["2014":"2016"]
[20]:
date temperaturemin temperaturemax precipitation snowfall snowdepth avgwindspeed fastest2minwinddir fastest2minwindspeed fastest5secwinddir ... drizzle snow freezingrain smokehaze thunder highwind hail blowingsnow dust freezingfog
date
2014-01-01 2014-01-01 29.1 51.1 0.00 0.0 0.0 2.46 200.0 8.95 210.0 ... No No No No No No No No No No
2014-01-02 2014-01-02 37.0 48.9 0.33 0.0 0.0 2.68 310.0 12.97 320.0 ... No No No No No No No No No No
2014-01-03 2014-01-03 22.1 43.0 0.00 0.0 0.0 7.38 300.0 21.92 310.0 ... No No No No No No No No No No
2014-01-04 2014-01-04 19.2 37.9 0.00 0.0 0.0 2.24 120.0 8.05 120.0 ... No No No No No No No No No No
2014-01-05 2014-01-05 37.0 62.1 0.00 0.0 0.0 4.92 180.0 16.11 180.0 ... No No No No No No No No No No
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
2016-12-27 2016-12-27 52.0 68.0 0.01 0.0 0.0 10.29 240.0 19.91 230.0 ... No No No No No No No No No No
2016-12-28 2016-12-28 36.0 60.1 0.00 0.0 0.0 2.91 10.0 8.95 20.0 ... No No No No No No No No No No
2016-12-29 2016-12-29 39.0 63.0 0.35 0.0 0.0 7.83 290.0 21.92 320.0 ... No No No No No No No No No No
2016-12-30 2016-12-30 28.2 48.0 0.00 0.0 0.0 8.28 280.0 21.03 280.0 ... No No No No No No No No No No
2016-12-31 2016-12-31 23.2 50.0 0.00 0.0 0.0 8.72 220.0 21.03 230.0 ... No No No No No No No No No No

1096 rows × 28 columns

[7]:
# Aggregations
df.loc["2015"].temperaturemin.mean()
[7]:
51.70246575342466
[16]:
# Conditional operatives
df.loc["2015"].temperaturemin.min() > df.loc["2016"].temperaturemin.min()
[16]:
False
[9]:
# Slicing
df.loc["2015":"2019"]
df.index.dtype
[9]:
dtype('<M8[ns]')

Actividades

  1. ¿Cuántas veces ha nevado por año (snowfall)?

[21]:
pd.__version__
[21]:
'2.2.2'
[ ]:
df.groupby(df.index.year).agg()
<bound method DataFrameGroupBy.aggregate of <pandas.core.groupby.generic.DataFrameGroupBy object at 0x1059fa950>>
  1. ¿En qué año se han registrado más nieve (snowdepth)?

[ ]:

  1. Crea un dataframe que contenga la temperatura máxima de julio por cada año.

  1. Haz una agrupación que contenga las temperaturas máximas y mínimas de cada mes de cada año.

Pivotar una tabla consiste en organizar las columnas a filas o las filas a columnas. Con ello disponemos los datos transpuestos a la modelización original.

Enlace a la documentación: - https://pandas.pydata.org/docs/reference/api/pandas.pivot_table.html - https://pandas.pydata.org/docs/reference/api/pandas.pivot.html

[25]:
import pandas as pd
import numpy as np

samples=5
df= pd.DataFrame(
    {
        "Municipio":np.repeat(["muni%i"%i for i in range(samples)],3) ,
        "Categoria"   :["Inscritos","Censo","Población"]*(samples),
        "Values"   : np.random.randint(1,10,samples*3)
    })


print(df)
   Municipio  Categoria  Values
0      muni0  Inscritos       7
1      muni0      Censo       5
2      muni0  Población       7
3      muni1  Inscritos       5
4      muni1      Censo       9
5      muni1  Población       7
6      muni2  Inscritos       3
7      muni2      Censo       4
8      muni2  Población       2
9      muni3  Inscritos       6
10     muni3      Censo       3
11     muni3  Población       2
12     muni4  Inscritos       9
13     muni4      Censo       3
14     muni4  Población       5
[ ]:
df[df["Categoria"]=="Censo"].mean()
[32]:
# indexcolumn, Grouper, array, or list of the previous
# Keys to group by on the pivot table index. If a list is passed, it can contain any of the other types (except list).
# If an array is passed, it must be the same length as the data and will be used in the same manner as column values.
pd.pivot_table(df, index=['Categoria'],values="Values")
[32]:
Values
Categoria
Censo 4.8
Inscritos 6.0
Población 4.6
[3]:
# columnscolumn, Grouper, array, or list of the previous
# Keys to group by on the pivot table column. If a list is passed, it can contain any of the other types (except list).
# If an array is passed, it must be the same length as the data and will be used in the same manner as column values.
pd.pivot_table(df, columns=['Categoria'])
/var/folders/6j/7gfvt_29797dypw8t1wttblw0000gn/T/ipykernel_82835/1386358381.py:4: FutureWarning: pivot_table dropped a column because it failed to aggregate. This behavior is deprecated and will raise in a future version of pandas. Select only the columns that can be aggregated.
  pd.pivot_table(df, columns=['Categoria'])
[3]:
Categoria Censo Inscritos Población
Values 4.8 6.4 6.6
[34]:
df2 = pd.pivot_table(df, index=['Municipio'],columns=["Categoria"])
df2
[34]:
Values
Categoria Censo Inscritos Población
Municipio
muni0 5.0 7.0 7.0
muni1 9.0 5.0 7.0
muni2 4.0 3.0 2.0
muni3 3.0 6.0 2.0
muni4 3.0 9.0 5.0
[ ]:
df2["Censo"].mean() #Alerta, pivotar también genera multi-columas/indices.
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
File ~/.pyenv/versions/3.11.0rc2/envs/my3110/lib/python3.11/site-packages/pandas/core/indexes/base.py:3805, in Index.get_loc(self, key)
   3804 try:
-> 3805     return self._engine.get_loc(casted_key)
   3806 except KeyError as err:

File index.pyx:167, in pandas._libs.index.IndexEngine.get_loc()

File index.pyx:196, in pandas._libs.index.IndexEngine.get_loc()

File pandas/_libs/hashtable_class_helper.pxi:7081, in pandas._libs.hashtable.PyObjectHashTable.get_item()

File pandas/_libs/hashtable_class_helper.pxi:7089, in pandas._libs.hashtable.PyObjectHashTable.get_item()

KeyError: 'Censo'

The above exception was the direct cause of the following exception:

KeyError                                  Traceback (most recent call last)
Cell In[35], line 1
----> 1 df2["Censo"].mean()

File ~/.pyenv/versions/3.11.0rc2/envs/my3110/lib/python3.11/site-packages/pandas/core/frame.py:4101, in DataFrame.__getitem__(self, key)
   4099 if is_single_key:
   4100     if self.columns.nlevels > 1:
-> 4101         return self._getitem_multilevel(key)
   4102     indexer = self.columns.get_loc(key)
   4103     if is_integer(indexer):

File ~/.pyenv/versions/3.11.0rc2/envs/my3110/lib/python3.11/site-packages/pandas/core/frame.py:4159, in DataFrame._getitem_multilevel(self, key)
   4157 def _getitem_multilevel(self, key):
   4158     # self.columns is a MultiIndex
-> 4159     loc = self.columns.get_loc(key)
   4160     if isinstance(loc, (slice, np.ndarray)):
   4161         new_columns = self.columns[loc]

File ~/.pyenv/versions/3.11.0rc2/envs/my3110/lib/python3.11/site-packages/pandas/core/indexes/multi.py:3040, in MultiIndex.get_loc(self, key)
   3037     return mask
   3039 if not isinstance(key, tuple):
-> 3040     loc = self._get_level_indexer(key, level=0)
   3041     return _maybe_to_slice(loc)
   3043 keylen = len(key)

File ~/.pyenv/versions/3.11.0rc2/envs/my3110/lib/python3.11/site-packages/pandas/core/indexes/multi.py:3391, in MultiIndex._get_level_indexer(self, key, level, indexer)
   3388         return slice(i, j, step)
   3390 else:
-> 3391     idx = self._get_loc_single_level_index(level_index, key)
   3393     if level > 0 or self._lexsort_depth == 0:
   3394         # Desired level is not sorted
   3395         if isinstance(idx, slice):
   3396             # test_get_loc_partial_timestamp_multiindex

File ~/.pyenv/versions/3.11.0rc2/envs/my3110/lib/python3.11/site-packages/pandas/core/indexes/multi.py:2980, in MultiIndex._get_loc_single_level_index(self, level_index, key)
   2978     return -1
   2979 else:
-> 2980     return level_index.get_loc(key)

File ~/.pyenv/versions/3.11.0rc2/envs/my3110/lib/python3.11/site-packages/pandas/core/indexes/base.py:3812, in Index.get_loc(self, key)
   3807     if isinstance(casted_key, slice) or (
   3808         isinstance(casted_key, abc.Iterable)
   3809         and any(isinstance(x, slice) for x in casted_key)
   3810     ):
   3811         raise InvalidIndexError(key)
-> 3812     raise KeyError(key) from err
   3813 except TypeError:
   3814     # If we have a listlike key, _check_indexing_error will raise
   3815     #  InvalidIndexError. Otherwise we fall through and re-raise
   3816     #  the TypeError.
   3817     self._check_indexing_error(key)

KeyError: 'Censo'
[5]:
# aggfuncfunction, list of functions, dict, default “mean”
# If a list of functions is passed, the resulting pivot table will have hierarchical columns whose top level are the function names
#  (inferred from the function objects themselves).
pd.pivot_table(df, index=['Categoria'], aggfunc=sum)

/var/folders/6j/7gfvt_29797dypw8t1wttblw0000gn/T/ipykernel_82835/1620381455.py:4: FutureWarning: The operation <built-in function sum> failed on a column. If any error is raised, this will raise an exception in a future version of pandas. Drop these columns to avoid this warning.
  pd.pivot_table(df, index=['Categoria'], aggfunc=sum)
[5]:
Values
Categoria
Censo 24
Inscritos 32
Población 33
[ ]: