Удалить столбец из панды DataFrame

1334

При удалении столбца в DataFrame я использую:

del df['column_name']

И это прекрасно работает. Почему я не могу использовать следующее?

del df.column_name

Поскольку можно получить доступ к столбцу / серии как df.column_name, я ожидал, что это сработает.

Джон
источник
2
Обратите внимание, что этот вопрос обсуждается в Meta .
RM

Ответы:

864

Как вы уже догадались, правильный синтаксис

del df['column_name']

Трудно заставить del df.column_nameработать просто из-за синтаксических ограничений в Python. del df[name]переводится на df.__delitem__(name)под прикрытием Python.

Уэс МакКинни
источник
25
Я понимаю, что это очень старый «ответ», но мое любопытство задето - почему это синтаксическое ограничение Python? class A(object): def __init__(self): self.var = 1устанавливает класс, затем a = A(); del a.varработает просто отлично ...
dwanderson
14
@dwanderson Разница в том, что когда столбец должен быть удален, DataFrame должен иметь свою собственную обработку для «как это сделать». В случае del df[name], он получает перевод, df.__delitem__(name)который представляет собой метод, который DataFrame может реализовать и изменить в соответствии со своими потребностями. В случае del df.name, переменная-член удаляется без каких-либо шансов на выполнение какого-либо пользовательского кода. Рассмотрите свой собственный пример - можете ли вы получить del a.varрезультат «удаления переменной»? Если можешь, расскажи как. Я не могу :)
Йонатан
8
@Yonatan Вы можете использовать либо docs.python.org/3/reference/datamodel.html#object.__delattr__, либо дескрипторы для этого: docs.python.org/3/howto/descriptor.html
Евгений Пахомов
5
Комментарий @Yonatan Eugene относится и к Python 2; дескрипторы были в Python 2 с 2.2, и это тривиально, чтобы удовлетворить ваши требования;)
CS
1
Этот ответ не совсем правильный - pandasразработчики не сделали этого , но это не значит, что это трудно сделать.
wizzwizz4
2187

Лучший способ сделать это в пандах - это использовать drop:

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

где 1есть ось число ( 0для строк и 1столбцов.)

Чтобы удалить столбец без переназначения, dfвы можете сделать:

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

Наконец, чтобы упасть по номеру столбца, а не по метке столбца , попробуйте это удалить, например, 1-й, 2-й и 4-й столбцы:

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

Также работает с «текстовым» синтаксисом для столбцов:

df.drop(['column_nameA', 'column_nameB'], axis=1, inplace=True)
LondonRob
источник
79
Рекомендуется ли это delпо какой-то причине?
beardc
20
Хотя этот метод удаления имеет свои достоинства, этот ответ на самом деле не отвечает на заданный вопрос.
Пол
109
Верно @Paul, но из-за названия вопроса большинство людей, приезжающих сюда, сделают это, пытаясь решить, как удалить столбец.
LondonRob
24
@beardc еще одним преимуществом dropover delявляется то, что dropвы можете отбрасывать несколько столбцов одновременно, выполнять операцию на месте или нет, а также удалять записи вдоль любой оси (особенно полезно для трехмерной матрицы или Panel)
варочные поверхности
8
Еще одно преимущество dropнад delтом , что падение является частью панд API и содержит документацию.
модуль
242

Использование:

columns = ['Col1', 'Col2', ...]
df.drop(columns, inplace=True, axis=1)

Это удалит один или несколько столбцов на месте. Обратите внимание, что он inplace=Trueбыл добавлен в pandas v0.13 и не будет работать на старых версиях. Вы должны были бы присвоить результат обратно в этом случае:

df = df.drop(columns, axis=1)
Кришна Санкар
источник
3
Примечание об этом ответе: если используется «список», квадратные скобки должны быть df.drop(list,inplace=True,axis=1)
убраны
1
это действительно должен быть принятый ответ, потому что он ясно показывает преимущество этого метода del- он может отбрасывать более одного столбца одновременно.
dbliss
111

Падение по индексу

Удалить первый, второй и четвертый столбцы:

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

Удалить первый столбец:

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

Существует необязательный параметр inplace, позволяющий изменять исходные данные без создания копии.

Popped

Выбор, добавление, удаление столбцов

Удалить столбец column-name:

df.pop('column-name')

Примеры:

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
jezrael
источник
1
Как я могу вставить ряд в панд?
Кеннет Селеста
2
@Yugi Вы можете использовать для этого транспонированный фрейм данных. бывший - df.T.pop('A')
Часы Раб
@ClockSlave Это не меняет оригинал df. Вы могли бы сделать, df = df.T; df.pop(index); df = df.Tно это кажется чрезмерным.
CS95
Вместо того, df.drop(df.columns[[0]], axis=1, inplace=True)чтобы не было достаточно использовать df.drop([0], axis=1)?
Анирбан Мукерджи
1
@ Anirban Mukherjee Это зависит. Если хотите удалить имя столбца 0, то df.drop(0, axis=1)работает хорошо. Но если вы не знаете имя столбца и df.drop(df.columns[[0]], axis=1, inplace=True)хотите удалить первый столбец, тогда нужно выбрать первый столбец по позиции и отбросить его.
jezrael
71

Фактический вопрос, который пропущен большинством ответов здесь:

Почему я не могу использовать del df.column_name?

Сначала нам нужно понять проблему, которая требует от нас погрузиться в магические методы Python .

Как Уэс указывает в своем ответе, он del df['column']отображает магический метод питона, df.__delitem__('column')который реализован в пандах для удаления столбца.

Однако, как указано в ссылке выше о магических методах Python :

Фактически, __del__почти никогда не должен использоваться из-за сомнительных обстоятельств, при которых это называют; используйте это с осторожностью!

Вы можете утверждать, что del df['column_name']не должны использоваться или поощряться, и, следовательно, del df.column_nameне должны даже рассматриваться.

Однако, в теории, del df.column_nameможно было бы implemeted работать в панд , используя в метод волшебной__delattr__ . Это, однако, создает определенные проблемы, проблемы, которые del df['column_name']уже есть у реализации, но в меньшей степени.

Пример задачи

Что, если я определю столбец в кадре данных, который называется «dtypes» или «columns».

Тогда предположим, что я хочу удалить эти столбцы.

del df.dtypesможет привести к __delattr__путанице в методе, как если бы он удалил атрибут "dtypes" или столбец "dtypes".

Архитектурные вопросы, стоящие за этой проблемой

  1. Является ли датафрейм коллекцией столбцов ?
  2. Является ли датафрейм набором строк ?
  3. Является ли столбец атрибутом кадра данных?

Панды отвечает:

  1. Да, во всех отношениях
  2. Нет, но если вы хотите, чтобы это было, вы можете использовать методы .ix, .locили .iloc.
  3. Может быть, вы хотите прочитать данные? Тогда да , если только имя атрибута не занято другим атрибутом, принадлежащим фрейму данных. Вы хотите изменить данные? Тогда нет .

TLDR;

Вы не можете этого сделать, del df.column_nameпотому что у pandas довольно дико развитая архитектура, которую необходимо пересмотреть, чтобы такого рода когнитивный диссонанс не возникал у пользователей.

Protip:

Не используйте df.column_name, это может быть красиво, но это вызывает когнитивный диссонанс

Цитаты Zen of Python, которые здесь вписываются:

Существует несколько способов удаления столбца.

Должен быть один - и желательно только один - очевидный способ сделать это.

Столбцы иногда являются атрибутами, но иногда нет.

Особые случаи не достаточно особенные, чтобы нарушать правила.

Есть ли del df.dtypesудалить атрибут dtypes или столбец dtypes?

Перед лицом двусмысленности откажитесь от соблазна гадать.

firelynx
источник
«На самом деле, __del__почти никогда не следует использовать из-за сомнительных обстоятельств, при которых он вызывается; используйте его с осторожностью!» здесь совершенно не имеет значения, так как используемый здесь метод __delattr__.
pppery
1
@ppperry вы не цитируете. это delвстроенная , что имеется в виду, а не .__del__метод экземпляра. delВстроенная команда отображение на __delattr__и __delitem__что то , что я строю свой аргумент на. Так что, может быть, вы хотите перечитать то, что я написал.
firelynx
1
__... __получает StackExchange как смелую разметку
pppery
2
«Не используйте df.column_name, это может быть красиво, но это вызывает когнитивный диссонанс» Что это значит? Я не психолог, поэтому мне нужно поискать это, чтобы понять, что вы имеете в виду. Кроме того, цитирование Дзэн не имеет смысла, потому что существуют сотни способов сделать то же самое в пандах.
CS95
58

Приятным дополнением является возможность отбрасывать столбцы, только если они существуют . Таким образом, вы можете охватить больше вариантов использования, и он удалит только существующие столбцы из меток, переданных ему:

Просто добавьте ошибки = игнорировать , например:

df.drop(['col_name_1', 'col_name_2', ..., 'col_name_N'], inplace=True, axis=1, errors='ignore')
  • Это новинка от панд 0.16.1 и выше. Документация здесь .
EiTan LaVi
источник
41

с версии 0.16.1 вы можете сделать

df.drop(['column_name'], axis = 1, inplace = True, errors = 'ignore')
sushmit
источник
3
И это также поддерживает удаление нескольких столбцов, некоторые из которых не должны существовать (то есть без возникновения ошибки errors= 'ignore') df.drop(['column_1','column_2'], axis=1 , inplace=True,errors= 'ignore'), если такое приложение желательно!
мюон
31

Хорошей практикой всегда является использование []обозначений. Одна из причин заключается в том, что атрибут notation ( df.column_name) не работает для пронумерованных индексов:

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
Энди Хейден
источник
26

Панды 0.21+ ответ

Панды версия 0,21 изменила dropнемного способа включать как indexи columnsпараметры соответствуют Сигнатуре renameи reindexметодов.

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

Лично я предпочитаю использовать axisпараметр для обозначения столбцов или индекса, поскольку это основной параметр ключевого слова, используемый почти во всех методах панд. Но теперь у вас есть несколько добавленных вариантов в версии 0.21.

Тед Петру
источник
1
df.drop (['column_a', 'column_c'], axis = 1) | это работает для меня на данный момент
YouAreAwesome
21

В pandas 0.16.1+ вы можете отбрасывать столбцы, только если они существуют в соответствии с решением, опубликованным @eiTanLaVi. До этой версии вы можете достичь того же результата с помощью понимания условного списка:

df.drop([col for col in ['col_name_1','col_name_2',...,'col_name_N'] if col in df], 
        axis=1, inplace=True)
Александр
источник
14

TL; DR

Много усилий, чтобы найти чуть более эффективное решение. Трудно оправдать добавленную сложность, жертвуя простотойdf.drop(dlst, 1, errors='ignore')

df.reindex_axis(np.setdiff1d(df.columns.values, dlst), 1)

Преамбула
Удаление столбца семантически аналогично выбору других столбцов. Я покажу несколько дополнительных методов для рассмотрения.

Я также сосредоточусь на общем решении удаления нескольких столбцов одновременно и разрешения попытки удалить столбцы, которых нет.

Использование этих решений носит общий характер и будет работать и для простого случая.


Настройка
Рассмотрите pd.DataFrame dfи список для удаления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']

Результат должен выглядеть так:

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

Поскольку я приравниваю удаление столбца к выбору других столбцов, я разделю его на два типа:

  1. Выбор метки
  2. Логический выбор

Выбор метки

Мы начнем с изготовления списка / массива меток, которые представляют столбцы, которые мы хотим сохранить, и без столбцов, которые мы хотим удалить.

  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']

Столбцы из ярлыков
Для сравнения процесса выбора предположим:

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

Тогда мы можем оценить

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

Которые все оценивают:

   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

Логический ломтик

Мы можем построить массив / список логических значений для нарезки

  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)

Колонны от Boolean
Для сравнения

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

Которые все оценивают:

   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

Надежные сроки

функции

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)

тестирование

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()

Это относительно времени, которое требуется для запуска df.drop(dlst, 1, errors='ignore'). Кажется, что после всех этих усилий мы лишь незначительно улучшаем производительность.

введите описание изображения здесь

Если факт, то лучшие решения использовать reindexили reindex_axisвзломать list(set(df.columns.values.tolist()).difference(dlst)). Близкая секунда и все же очень незначительно лучше, чем dropесть 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
piRSquared
источник
2

Точечный синтаксис работает в JavaScript, но не в Python.

  • Python: del df['column_name']
  • JavaScript: del df['column_name'] или del df.column_name
Доктор
источник
2

Если ваш исходный фрейм данных dfне слишком большой, у вас нет ограничений памяти, и вам нужно всего лишь сохранить несколько столбцов, тогда вы можете также создать новый фрейм данных только с нужными вам столбцами:

new_df = df[['spam', 'sausage']]
ccpizza
источник
2

Мы можем удалить или удалить указанный столбец или расширенные столбцы методом drop () .

Предположим, что df является фреймом данных.

Удаляемый столбец = column0

Код:

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

Удалить несколько столбцов col1, col2,. , , Колон, мы должны вставить все столбцы, которые должны быть удалены в списке. Затем удалите их методом drop ().

Код:

df = df.drop([col1, col2, . . . , coln], axis=1)

Я надеюсь, что это будет полезно.

Литтин Раджан
источник
df = df.drop([col1, col2, . . . , coln], axis=1)это не работает, если я указываю имя переменной вместо col1, col2 и т. д. Я получаю столбец ошибки не по оси, когда он определенно присутствует. @ Литтин Не могли бы вы помочь?
RSM
1

Еще один способ удаления столбца в Pandas DataFrame

если вы не ищете удаление на месте, то вы можете создать новый DataFrame, указав столбцы, используя DataFrame(...)функцию как

my_dict = { 'name' : ['a','b','c','d'], 'age' : [10,20,25,22], 'designation' : ['CEO', 'VP', 'MD', 'CEO']}

df = pd.DataFrame(my_dict)

Создайте новый DataFrame как

newdf = pd.DataFrame(df, columns=['name', 'age'])

Вы получаете такой же хороший результат, как и результат del / drop

Дакш
источник
1
Это технически правильно, но кажется глупым перечислять каждый столбец, который нужно сохранить, а не только один (или несколько) столбцов, которые вы хотите удалить.
CS95