Pregunta Eliminar columna de pandas DataFrame utilizando del df.column_name


Al eliminar una columna en un DataFrame utilizo:

del df['column_name']

Y esto funciona genial ¿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.


814
2017-11-16 06:26


origen


Respuestas:


Es difícil de hacer del df.column_name funcionan simplemente como el resultado de limitaciones sintácticas en Python. del df[name] se traduce a df.__delitem__(name) bajo las sábanas de Python.


463
2017-11-21 03:12



La mejor manera de hacer esto en pandas es usar drop:

df = df.drop('column_name', 1)

dónde 1 es el eje número (0 para filas y 1 para columnas.)

Para eliminar la columna sin tener que reasignar df tu puedes hacer:

df.drop('column_name', axis=1, inplace=True)

Finalmente, para pasar a la columna número en lugar de por columna etiqueta, intenta esto para eliminar, p. la 1ra, 2da y 4ta columnas:

df.drop(df.columns[[0, 1, 3]], axis=1)  # df.columns is zero-based pd.Index 

1529
2017-08-09 11:12



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 fue agregado en pandas v0.13 y no funcionará en versiones anteriores. Tendría que asignar el resultado de nuevo en ese caso:

df = df.drop(columns, axis=1)

178
2018-03-23 20:57



Caída por índice

Eliminar la primera, segunda y cuarta columnas:

df.drop(df.columns[[0,1,3]], axis=1, inplace=True)

Eliminar la primera columna:

df.drop(df.columns[[0]], axis=1, inplace=True)

Hay un parámetro opcional inplace para que el original los datos pueden ser modificados sin crear una copia.

Estallado

Selección de columna, 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

78
2017-07-15 13:37



La pregunta real planteada, pasada por alto por la mayoría de las respuestas aquí es:

Por qué no puedo usar del df.column_name?

Al principio tenemos que entender el problema, lo que requiere que nos sumerjamos en métodos mágicos de pitón.

Como Wes señala en su respuesta del df['column'] mapas a la pitón método mágico  df.__delitem__('column') cual es implementado en pandas para soltar la columna

Sin embargo, como se señala en el enlace de arriba sobre métodos mágicos de pitón:

De hecho, del casi nunca debe usarse debido a las circunstancias precarias bajo las cuales se llama; Úselo con precaución!

Podrías argumentar que del df['column_name'] no debe ser utilizado o alentado, y por lo tanto del df.column_name ni siquiera debería ser considerado.

Sin embargo, en teoría, del df.column_name podría ser implementado para trabajar en pandas usando el método mágico __delattr__. Sin embargo, esto introduce ciertos problemas, problemas que el del df['column_name'] implementación ya tiene, pero en menor grado.

Problema de ejemplo

¿Qué ocurre si defino una columna en un marco de datos llamado "dtypes" o "columnas"?

Entonces asuma que quiero eliminar estas columnas.

del df.dtypes Haría el __delattr__ método confundido como si debería eliminar el atributo "dtypes" o la columna "dtypes".

Preguntas arquitectónicas detrás de este problema

  1. Es un marco de datos colección de columnas?
  2. Es un marco de datos una colección de filas?
  3. Es una columna atributo de un marco de datos?

Pandas responde:

  1. Sí, en todos los sentidos
  2. No, pero si quieres que sea, puedes usar .ix, .loc o .iloc métodos.
  3. Tal vez, ¿quieres leer ¿datos? Entonces , a no ser que el nombre del atributo ya está ocupado por otro atributo que pertenece al marco de datos. Quieres modificar ¿datos? Entonces no.

TLDR;

Tú no puedes hacer del df.column_name porque los pandas tienen una arquitectura bastante desarrollada que debe ser reconsiderada para que este tipo de disonancia cognitiva no ocurrir a sus usuarios.

Protip:

No use df.column_name, puede ser bonito, pero causa disonancia cognitiva

Citas de Zen of Python que encajan aquí:

Hay varias formas de eliminar una columna.

Debería haber una, y preferiblemente solo una, forma obvia de hacerlo.

Las columnas son a veces atributos, pero a veces no.

Los casos especiales no son lo suficientemente especiales como para romper las reglas.

Hace del df.dtypes eliminar el atributo dtypes o la columna dtypes?

En vista de la ambigüedad, rechace la tentación de adivinar.


54
2018-05-03 09:48



desde la versión 0.16.1 puedes hacer

df.drop(['column_name'], axis = 1, inplace = True, errors = 'ignore')

37
2018-04-30 18:57



Una buena adición es la capacidad de soltar columnas solo si existen. De esta forma puede cubrir más casos de uso, y solo eliminará las columnas existentes de las etiquetas que se le pasen:

Simplemente agregue errors = 'ignorar', por ejemplo.:

df.drop(['col_name_1', 'col_name_2', ..., 'col_name_N'], inplace=True, axis=1, errors='ignore')
  • Esto es nuevo desde pandas 0.16.1 en adelante. La documentación es aquí.

37
2018-01-03 12:29



Es una buena práctica usar siempre el [] notación. Una razón es 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

24
2017-11-16 11:33



En pandas 0.16.1+ puedes soltar 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)

20
2018-02-13 21:58



TL; DR

Un gran esfuerzo para encontrar una solución marginalmente más eficiente. Difícil de justificar la complejidad añadida sacrificando 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 enfocaré en la solución general de eliminar múltiples 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
Considera el pd.DataFrame  df y lista para borrar 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 debería verse así:

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

Como equiparo eliminar una columna para seleccionar las otras columnas, la dividiré en dos tipos:

  1. Selección de etiqueta
  2. Selección booleana

Selección de etiqueta

Comenzamos fabricando la lista / conjunto de etiquetas que representan las columnas que queremos conservar y sin las columnas que queremos eliminar.

  1. df.columns.difference(dlst)

    Index(['A', 'B', 'C', 'D', 'E', 'F', 'G'], dtype='object')
    
  2. np.setdiff1d(df.columns.values, dlst)

    array(['A', 'B', 'C', 'D', 'E', 'F', 'G'], dtype=object)
    
  3. df.columns.drop(dlst, errors='ignore')

    Index(['A', 'B', 'C', 'D', 'E', 'F', 'G'], dtype='object')
    
  4. list(set(df.columns.values.tolist()).difference(dlst))

    # does not preserve order
    ['E', 'D', 'B', 'F', 'G', 'A', 'C']
    
  5. [x for x in df.columns.values.tolist() if x not in dlst]

    ['A', 'B', 'C', 'D', 'E', 'F', 'G']
    

Columnas de etiquetas
Por el bien de comparar el proceso de selección, suponga:

 cols = [x for x in df.columns.values.tolist() if x not in dlst]

Entonces podemos evaluar

  1. df.loc[:, cols]
  2. df[cols]
  3. df.reindex(columns=cols)
  4. df.reindex_axis(cols, 1)

Todos los cuales 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 cortar

  1. ~df.columns.isin(dlst)
  2. ~np.in1d(df.columns.values, dlst)
  3. [x not in dlst for x in df.columns.values.tolist()]
  4. (df.columns.values[:, None] != dlst).all(1)

Columnas de Boolean
Por el bien de la comparación

bools = [x not in dlst for x in df.columns.values.tolist()]
  1. df.loc[: bools]

Todos los cuales 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 correr df.drop(dlst, 1, errors='ignore'). Parece que después de todo ese esfuerzo, solo mejoramos el rendimiento modestamente.

enter image description here

De hecho, las mejores soluciones usan reindex o reindex_axis en el hack list(set(df.columns.values.tolist()).difference(dlst)). Un segundo cercano y aún muy marginalmente mejor que 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

11
2017-09-20 05:43



Pandas 0.21+ respuesta

La versión 0.21 de Pandas ha cambiado ligeramente el método de caída para incluir tanto index y columns parámetros para que coincida con la firma de la rename y reindex métodos.

df.drop(columns=['column_a', 'column_c'])

Personalmente, prefiero usar el axis parámetro para denotar columnas o índice porque es el parámetro de palabra clave predominante usado en casi todos los métodos de pandas. Pero ahora tiene algunas opciones adicionales en la versión 0.21.


10
2017-10-24 14:31