python - index - pandas.at versus.loc
pandas python (2)
A medida que preguntaste acerca de las limitaciones de .at
, aquí hay una cosa con la que me topé recientemente (usar pandas 0.22). Usemos el ejemplo de la documentación :
df = pd.DataFrame([[0, 2, 3], [0, 4, 1], [10, 20, 30]], index=[4, 5, 6], columns=[''A'', ''B'', ''C''])
df2 = df.copy()
A B C
4 0 2 3
5 0 4 1
6 10 20 30
Si ahora hago
df.at[4, ''B''] = 100
el resultado se ve como se esperaba
A B C
4 0 100 3
5 0 4 1
6 10 20 30
Sin embargo, cuando trato de hacer
df.at[4, ''C''] = 10.05
Parece que .at
intenta conservar el tipo de datos (aquí: int
) :
A B C
4 0 100 10
5 0 4 1
6 10 20 30
Eso parece ser una diferencia para .loc
:
df2.loc[4, ''C''] = 10.05
cede lo deseado
A B C
4 0 2 10.05
5 0 4 1.00
6 10 20 30.00
Lo arriesgado en el ejemplo anterior es que sucede en silencio (la conversión de float
a int`). Cuando uno intente lo mismo con las cadenas, lanzará un error:
df.at[5, ''A''] = ''a_string''
ValueError: literal no válido para int () con base 10: ''a_string''
He estado explorando cómo optimizar mi código y me encontré con el método .at
pandas
. Por la documentation
Acceso escalar basado en etiquetas rápidas
Al igual que en loc, at proporciona búsquedas escalares basadas en etiquetas. También puede establecer utilizando estos indexadores.
Así que corrí algunas muestras:
Preparar
import pandas as pd
import numpy as np
from string import letters, lowercase, uppercase
lt = list(letters)
lc = list(lowercase)
uc = list(uppercase)
def gdf(rows, cols, seed=None):
"""rows and cols are what you''d pass
to pd.MultiIndex.from_product()"""
gmi = pd.MultiIndex.from_product
df = pd.DataFrame(index=gmi(rows), columns=gmi(cols))
np.random.seed(seed)
df.iloc[:, :] = np.random.rand(*df.shape)
return df
seed = [3, 1415]
df = gdf([lc, uc], [lc, uc], seed)
print df.head().T.head().T
df
parece a
a
A B C D E
a A 0.444939 0.407554 0.460148 0.465239 0.462691
B 0.032746 0.485650 0.503892 0.351520 0.061569
C 0.777350 0.047677 0.250667 0.602878 0.570528
D 0.927783 0.653868 0.381103 0.959544 0.033253
E 0.191985 0.304597 0.195106 0.370921 0.631576
Vamos a usar .at
y .loc
y asegurar que obtengo lo mismo
print "using .loc", df.loc[(''a'', ''A''), (''c'', ''C'')]
print "using .at ", df.at[(''a'', ''A''), (''c'', ''C'')]
using .loc 0.37374090276
using .at 0.37374090276
Velocidad de prueba usando .loc
%%timeit
df.loc[(''a'', ''A''), (''c'', ''C'')]
10000 loops, best of 3: 180 µs per loop
Velocidad de prueba usando .at
%%timeit
df.at[(''a'', ''A''), (''c'', ''C'')]
The slowest run took 6.11 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 8 µs per loop
Esto parece ser un gran aumento de velocidad. Incluso en la etapa de almacenamiento en caché 6.11 * 8
es mucho más rápido que 180
Pregunta
¿Cuáles son las limitaciones de .at
? Estoy motivado para usarlo. La documentación dice que es similar a .loc
pero no se comporta de manera similar. Ejemplo:
# small df
sdf = gdf([lc[:2]], [uc[:2]], seed)
print sdf.loc[:, :]
A B
a 0.444939 0.407554
b 0.460148 0.465239
donde como print sdf.at[:, :]
da como resultado TypeError: unhashable type
Así que obviamente no es lo mismo, incluso si la intención es ser similar.
Dicho esto, ¿quién puede proporcionar orientación sobre qué se puede y no se puede hacer con el método .at
?
Actualización: df.get_value
está en desuso a partir de la versión 0.21.0. El uso de df.at
o df.iat
es el método recomendado para seguir adelante.
df.at
solo puede acceder a un único valor a la vez.
df.loc
puede seleccionar múltiples filas y / o columnas.
Tenga en cuenta que también hay df.get_value
, que puede ser incluso más rápido para acceder a valores individuales:
In [25]: %timeit df.loc[(''a'', ''A''), (''c'', ''C'')]
10000 loops, best of 3: 187 µs per loop
In [26]: %timeit df.at[(''a'', ''A''), (''c'', ''C'')]
100000 loops, best of 3: 8.33 µs per loop
In [35]: %timeit df.get_value((''a'', ''A''), (''c'', ''C''))
100000 loops, best of 3: 3.62 µs per loop
Debajo del capó, df.at[...]
llama a df.get_value
, pero también realiza una comprobación de tipo de las teclas.