python - index - pandas dataframe tutorial
Elimine la columna de Pandas DataFrame usando del df.column_name (14)
Pandas 0.21+ respuesta
La versión 0.21 de Pandas ha cambiado el método de drop
ligeramente para incluir los parámetros de index
y columns
para que coincida con la firma de los métodos de rename
y reindex
.
df.drop(columns=[''column_a'', ''column_c''])
Personalmente, prefiero usar el parámetro axis
para denotar columnas o índice porque es el parámetro de palabra clave predominante utilizado en casi todos los métodos pandas. Pero, ahora tienes algunas opciones agregadas en la versión 0.21.
Cuando borro una columna en un DataFrame, uso:
del df[''column_name'']
Y esto funciona muy bien. ¿Por qué no puedo usar lo siguiente?
del df.column_name
Como puede acceder a la columna / Serie como df.column_name
, espero que esto funcione.
TL; DR
Mucho esfuerzo para encontrar una solución marginalmente más eficiente. Es difícil justificar la complejidad agregada mientras se sacrifica la simplicidad de df.drop(dlst, 1, errors=''ignore'')
df.reindex_axis(np.setdiff1d(df.columns.values, dlst), 1)
Preámbulo
Eliminar una columna es semánticamente lo mismo que seleccionar las otras columnas. Mostraré algunos métodos adicionales para considerar.
También me centraré en la solución general de eliminar varias columnas a la vez y permitir el intento de eliminar columnas que no están presentes.
El uso de estas soluciones es general y también funcionará para el caso simple.
Preparar
Tenga en cuenta el df
pd.DataFrame
y la lista para eliminar dlst
df = pd.DataFrame(dict(zip(''ABCDEFGHIJ'', range(1, 11))), range(3))
dlst = list(''HIJKLM'')
df
A B C D E F G H I J
0 1 2 3 4 5 6 7 8 9 10
1 1 2 3 4 5 6 7 8 9 10
2 1 2 3 4 5 6 7 8 9 10
dlst
[''H'', ''I'', ''J'', ''K'', ''L'', ''M'']
El resultado debe verse como:
df.drop(dlst, 1, errors=''ignore'')
A B C D E F G
0 1 2 3 4 5 6 7
1 1 2 3 4 5 6 7
2 1 2 3 4 5 6 7
Ya que estoy igualando la eliminación de una columna para seleccionar las otras columnas, la dividiré en dos tipos:
- Selección de etiquetas
- Selección booleana
Selección de etiquetas
Comenzamos fabricando la lista / matriz de etiquetas que representan las columnas que queremos mantener y sin las columnas que queremos eliminar.
df.columns.difference(dlst)
Index([''A'', ''B'', ''C'', ''D'', ''E'', ''F'', ''G''], dtype=''object'')
np.setdiff1d(df.columns.values, dlst)
array([''A'', ''B'', ''C'', ''D'', ''E'', ''F'', ''G''], dtype=object)
df.columns.drop(dlst, errors=''ignore'')
Index([''A'', ''B'', ''C'', ''D'', ''E'', ''F'', ''G''], dtype=''object'')
list(set(df.columns.values.tolist()).difference(dlst))
# does not preserve order [''E'', ''D'', ''B'', ''F'', ''G'', ''A'', ''C'']
[x for x in df.columns.values.tolist() if x not in dlst]
[''A'', ''B'', ''C'', ''D'', ''E'', ''F'', ''G'']
Columnas de etiquetas
Para poder comparar el proceso de selección, asuma:
cols = [x for x in df.columns.values.tolist() if x not in dlst]
Entonces podemos evaluar
-
df.loc[:, cols]
-
df[cols]
-
df.reindex(columns=cols)
-
df.reindex_axis(cols, 1)
Que todos evalúan a:
A B C D E F G
0 1 2 3 4 5 6 7
1 1 2 3 4 5 6 7
2 1 2 3 4 5 6 7
Rebanada booleana
Podemos construir una matriz / lista de booleanos para rebanar
-
~df.columns.isin(dlst)
-
~np.in1d(df.columns.values, dlst)
-
[x not in dlst for x in df.columns.values.tolist()]
-
(df.columns.values[:, None] != dlst).all(1)
Columnas de booleano
Por el bien de la comparación
bools = [x not in dlst for x in df.columns.values.tolist()]
-
df.loc[: bools]
Que todos evalúan a:
A B C D E F G
0 1 2 3 4 5 6 7
1 1 2 3 4 5 6 7
2 1 2 3 4 5 6 7
Tiempo robusto
Funciones
setdiff1d = lambda df, dlst: np.setdiff1d(df.columns.values, dlst)
difference = lambda df, dlst: df.columns.difference(dlst)
columndrop = lambda df, dlst: df.columns.drop(dlst, errors=''ignore'')
setdifflst = lambda df, dlst: list(set(df.columns.values.tolist()).difference(dlst))
comprehension = lambda df, dlst: [x for x in df.columns.values.tolist() if x not in dlst]
loc = lambda df, cols: df.loc[:, cols]
slc = lambda df, cols: df[cols]
ridx = lambda df, cols: df.reindex(columns=cols)
ridxa = lambda df, cols: df.reindex_axis(cols, 1)
isin = lambda df, dlst: ~df.columns.isin(dlst)
in1d = lambda df, dlst: ~np.in1d(df.columns.values, dlst)
comp = lambda df, dlst: [x not in dlst for x in df.columns.values.tolist()]
brod = lambda df, dlst: (df.columns.values[:, None] != dlst).all(1)
Pruebas
res1 = pd.DataFrame(
index=pd.MultiIndex.from_product([
''loc slc ridx ridxa''.split(),
''setdiff1d difference columndrop setdifflst comprehension''.split(),
], names=[''Select'', ''Label'']),
columns=[10, 30, 100, 300, 1000],
dtype=float
)
res2 = pd.DataFrame(
index=pd.MultiIndex.from_product([
''loc''.split(),
''isin in1d comp brod''.split(),
], names=[''Select'', ''Label'']),
columns=[10, 30, 100, 300, 1000],
dtype=float
)
res = res1.append(res2).sort_index()
dres = pd.Series(index=res.columns, name=''drop'')
for j in res.columns:
dlst = list(range(j))
cols = list(range(j // 2, j + j // 2))
d = pd.DataFrame(1, range(10), cols)
dres.at[j] = timeit(''d.drop(dlst, 1, errors="ignore")'', ''from __main__ import d, dlst'', number=100)
for s, l in res.index:
stmt = ''{}(d, {}(d, dlst))''.format(s, l)
setp = ''from __main__ import d, dlst, {}, {}''.format(s, l)
res.at[(s, l), j] = timeit(stmt, setp, number=100)
rs = res / dres
rs
10 30 100 300 1000
Select Label
loc brod 0.747373 0.861979 0.891144 1.284235 3.872157
columndrop 1.193983 1.292843 1.396841 1.484429 1.335733
comp 0.802036 0.732326 1.149397 3.473283 25.565922
comprehension 1.463503 1.568395 1.866441 4.421639 26.552276
difference 1.413010 1.460863 1.587594 1.568571 1.569735
in1d 0.818502 0.844374 0.994093 1.042360 1.076255
isin 1.008874 0.879706 1.021712 1.001119 0.964327
setdiff1d 1.352828 1.274061 1.483380 1.459986 1.466575
setdifflst 1.233332 1.444521 1.714199 1.797241 1.876425
ridx columndrop 0.903013 0.832814 0.949234 0.976366 0.982888
comprehension 0.777445 0.827151 1.108028 3.473164 25.528879
difference 1.086859 1.081396 1.293132 1.173044 1.237613
setdiff1d 0.946009 0.873169 0.900185 0.908194 1.036124
setdifflst 0.732964 0.823218 0.819748 0.990315 1.050910
ridxa columndrop 0.835254 0.774701 0.907105 0.908006 0.932754
comprehension 0.697749 0.762556 1.215225 3.510226 25.041832
difference 1.055099 1.010208 1.122005 1.119575 1.383065
setdiff1d 0.760716 0.725386 0.849949 0.879425 0.946460
setdifflst 0.710008 0.668108 0.778060 0.871766 0.939537
slc columndrop 1.268191 1.521264 2.646687 1.919423 1.981091
comprehension 0.856893 0.870365 1.290730 3.564219 26.208937
difference 1.470095 1.747211 2.886581 2.254690 2.050536
setdiff1d 1.098427 1.133476 1.466029 2.045965 3.123452
setdifflst 0.833700 0.846652 1.013061 1.110352 1.287831
fig, axes = plt.subplots(2, 2, figsize=(8, 6), sharey=True)
for i, (n, g) in enumerate([(n, g.xs(n)) for n, g in rs.groupby(''Select'')]):
ax = axes[i // 2, i % 2]
g.plot.bar(ax=ax, title=n)
ax.legend_.remove()
fig.tight_layout()
Esto es relativo al tiempo que lleva ejecutar df.drop(dlst, 1, errors=''ignore'')
. Parece que después de todo ese esfuerzo, solo mejoramos modestamente el rendimiento.
Si es el caso, las mejores soluciones utilizan reindex_axis
o reindex_axis
en la list(set(df.columns.values.tolist()).difference(dlst))
. Un segundo cercano y aún muy marginalmente mejor que el drop
es np.setdiff1d
.
rs.idxmin().pipe(
lambda x: pd.DataFrame(
dict(idx=x.values, val=rs.lookup(x.values, x.index)),
x.index
)
)
idx val
10 (ridx, setdifflst) 0.653431
30 (ridxa, setdifflst) 0.746143
100 (ridxa, setdifflst) 0.816207
300 (ridx, setdifflst) 0.780157
1000 (ridxa, setdifflst) 0.861622
Caer por índice
Eliminar primera, segunda y cuarta columnas:
df.drop(df.columns[[0,1,3]], axis=1, inplace=True)
Eliminar primera columna:
df.drop(df.columns[[0]], axis=1, inplace=True)
Hay un parámetro opcional en el inplace
para que los datos originales puedan modificarse sin crear una copia.
Popped
Selección de columnas, adición, eliminación
Eliminar columna column-name
:
df.pop(''column-name'')
Ejemplos:
df = DataFrame.from_items([(''A'', [1, 2, 3]), (''B'', [4, 5, 6]), (''C'', [7,8, 9])], orient=''index'', columns=[''one'', ''two'', ''three''])
print df
:
one two three
A 1 2 3
B 4 5 6
C 7 8 9
df.drop(df.columns[[0]], axis=1, inplace=True)
print df
:
two three
A 2 3
B 5 6
C 8 9
three = df.pop(''three'')
print df
:
two
A 2
B 5
C 8
A partir de la versión 0.16.1 se puede hacer.
df.drop([''column_name''], axis = 1, inplace = True, errors = ''ignore'')
En pandas 0.16.1+ puede eliminar columnas solo si existen según la solución publicada por @eiTanLaVi. Antes de esa versión, puede lograr el mismo resultado a través de una lista de comprensión condicional:
df.drop([col for col in [''col_name_1'',''col_name_2'',...,''col_name_N''] if col in df],
axis=1, inplace=True)
Es difícil hacer que del df.column_name
funcione simplemente como resultado de las limitaciones sintácticas en Python. del df[name]
se traduce a df.__delitem__(name)
bajo las carátulas de Python.
Es una buena práctica usar siempre la notación []
. Una razón es que la notación de atributo ( df.column_name
) no funciona para los índices numerados:
In [1]: df = DataFrame([[1, 2, 3], [4, 5, 6]])
In [2]: df[1]
Out[2]:
0 2
1 5
Name: 1
In [3]: df.1
File "<ipython-input-3-e4803c0d1066>", line 1
df.1
^
SyntaxError: invalid syntax
La mejor manera de hacer esto en pandas es usar drop
:
df = df.drop(''column_name'', 1)
donde 1
es el número del eje ( 0
para filas y 1
para columnas).
Para eliminar la columna sin tener que reasignar df
puede hacer:
df.drop(''column_name'', axis=1, inplace=True)
Finalmente, para colocar por número de columna en lugar de por etiqueta de columna, intente esto para eliminar, por ejemplo, las columnas 1, 2 y 4:
df.drop(df.columns[[0, 1, 3]], axis=1) # df.columns is zero-based pd.Index
La pregunta real planteada, perdida por la mayoría de las respuestas aquí es:
¿Por qué no puedo usar del df.column_name
?
Al principio debemos entender el problema, que requiere que nos sumergamos en los métodos mágicos de los pitones .
Como lo señala Wes en su respuesta del df[''column'']
asigna al método mágico de python df.__delitem__(''column'')
que se implementa en pandas para eliminar la columna
Sin embargo, como se señaló en el enlace anterior sobre los métodos mágicos de Python :
De hecho, dell casi nunca debe usarse debido a las precarias circunstancias en que se llama; ¡Úsalo con precaución!
Podría argumentar que del df[''column_name'']
no se debe usar o alentar, y por lo tanto del df.column_name
ni siquiera se debe considerar.
Sin embargo, en teoría, del df.column_name
podría del df.column_name
para trabajar en pandas usando el método mágico __delattr__
. Sin embargo, esto introduce ciertos problemas, problemas que ya tiene la implementación del df[''column_name'']
, pero en menor grado.
Problema de ejemplo
¿Qué pasa si defino una columna en un marco de datos llamado "dtypes" o "columnas".
Entonces asume que quiero eliminar estas columnas.
del df.dtypes
confundiría el método __delattr__
como si eliminara el atributo "dtypes" o la columna "dtypes".
Preguntas arquitectónicas detrás de este problema.
- ¿Es un marco de datos una colección de columnas ?
- ¿Es un marco de datos una colección de filas ?
- ¿Es una columna un atributo de un marco de datos?
Pandas responde:
- Si en todos los sentidos
- No, pero si lo desea, puede usar los
.ix
,.loc
o.iloc
. - Tal vez, ¿quieres leer datos? Entonces sí , a menos que el nombre del atributo ya esté tomado por otro atributo que pertenece al marco de datos. ¿Quieres modificar los datos? Entonces no
TLDR;
No puede hacer del df.column_name
porque pandas tiene una arquitectura bastante desarrollada que debe reconsiderarse para que este tipo de disonancia cognitiva no ocurra a sus usuarios.
Protip:
No use df.column_name, puede ser bonito, pero causa disonancia cognitiva
Cotizaciones de Zen de Python que encaja aquí:
Hay varias formas de eliminar una columna.
Debe haber una, y preferiblemente solo una, obvia forma de hacerlo.
Las columnas son a veces atributos pero a veces no.
Los casos especiales no son lo suficientemente especiales para romper las reglas.
¿ del df.dtypes
elimina el atributo dtypes o la columna dtypes?
Ante la ambigüedad, rechace la tentación de adivinar.
La sintaxis de puntos funciona en JavaScript, pero no en Python.
- Python:
del df[''column_name'']
- JavaScript:
del df[''column_name'']
odel df.column_name
Si desea eliminar una sola columna ( col_name
) desde un marco de datos ( df
), intente uno de los siguientes:
df = df.drop(col_name, axis=1)
O
df.drop(col_name, axis=1, inplace=True)
Si desea eliminar una lista de columnas ( col_lst = [col_name_1,col_name_2,...]
) de un marco de datos ( df
), intente uno de los siguientes:
df.drop(col_lst, axis=1, inplace=True)
O
df.drop(columns=col_lst, inplace=True)
Una buena adición es la capacidad de eliminar columnas solo si existen . De esta manera puede cubrir más casos de uso, y solo eliminará las columnas existentes de las etiquetas que se le pasaron:
Simplemente agregue errores = ''ignorar'' , por ejemplo .:
df.drop([''col_name_1'', ''col_name_2'', ..., ''col_name_N''], inplace=True, axis=1, errors=''ignore'')
- Esto es nuevo a partir de pandas 0.16.1 en adelante. La documentación está here .
Utilizar:
columns = [''Col1'', ''Col2'', ...]
df.drop(columns, inplace=True, axis=1)
Esto eliminará una o más columnas en el lugar. Tenga en cuenta que inplace=True
se agregó en pandas v0.13 y no funcionará en versiones anteriores. Tendrías que volver a asignar el resultado en ese caso:
df = df.drop(columns, axis=1)
Otra forma de eliminar una columna en Pandas DataFrame
Si no está buscando una eliminación in situ, puede crear un nuevo DataFrame especificando las columnas usando la función DataFrame(...)
como
my_dict = { ''name'' : [''a'',''b'',''c'',''d''], ''age'' : [10,20,25,22], ''designation'' : [''CEO'', ''VP'', ''MD'', ''CEO'']}
df = pd.DataFrame(my_dict)
Crear un nuevo DataFrame como
newdf = pd.DataFrame(df, columns=[''name'', ''age''])
Obtienes un resultado tan bueno como lo que obtienes con del / drop