Как удалить столбец из DataFrame с некоторыми дополнительными подробностями

задняя часть

от пандDataFrameУдаление одного или нескольких столбцов — довольно распространенная задача, но оказывается, что существует множество возможных способов выполнения этой задачи. я нашелЭтот вопрос StackOverflow, а решения и обсуждения в них поднимают много интересных тем. Стоит углубиться в детали.

Во-первых, изDataFrameКаков «правильный» способ удаления столбца в ? Стандартный способ — думать на языке SQL и использоватьdrop.

import pandas as pd
import numpy as np

df = pd.DataFrame(np.arange(25).reshape((5,5)), columns=list("abcde"))

display(df)

try:
    df.drop('b')
except KeyError as ke:
    print(ke)
    a   b   c   d   e
0   0   1   2   3   4
1   5   6   7   8   9
2  10  11  12  13  14
3  15  16  17  18  19
4  20  21  22  23  24
"['b'] not found in axis"

Чего ждать? Почему я получаю сообщение об ошибке? Это потому чтоdropОсь работы по умолчанию — линии. Как и многие методы pandas, этот метод можно вызвать несколькими способами (некоторых это расстраивает).

ты можешь использоватьaxis=0илиaxis='rows'удалите строку или используйтеlabelsпараметр.

df.drop(0)                # drop a row, on axis 0 or 'rows'
df.drop(0, axis=0)        # same
df.drop(0, axis='rows')   # same
df.drop(labels=0)         # same
df.drop(labels=[0])       # same
    a   b   c   d   e
1   5   6   7   8   9
2  10  11  12  13  14
3  15  16  17  18  19
4  20  21  22  23  24

Опять же, как мы удаляем столбец?

Мы хотим удалить столбец, так как это выглядит? вы можете указатьaxisили используйтеcolumnsпараметр.

df.drop('b', axis=1)         # drop a column
df.drop('b', axis='columns') # same
df.drop(columns='b')         # same
df.drop(columns=['b'])       # same
    a   c   d   e
0   0   2   3   4
1   5   7   8   9
2  10  12  13  14
3  15  17  18  19
4  20  22  23  24

Ну, вот как вы опускаете столбец. Теперь вам нужно присвоить его новой переменной, либо вернуться к старой переменной, либо передатьinplace=True, чтобы сделать изменение постоянным.

df2 = df.drop('b', axis=1)

print(df2.columns)
print(df.columns)
Index(['a', 'c', 'd', 'e'], dtype='object')
Index(['a', 'b', 'c', 'd', 'e'], dtype='object')

Стоит отметить, что при использовании обоихindexиcolumnsпараметр, вы можете использовать drop, чтобы удалить как строки, так и столбцы, и вы можете передать несколько значений.

df.drop(index=[0,2], columns=['b','c'])
    a   d   e
1   5   8   9
3  15  18  19
4  20  23  24

Если у вас нет метода перетаскивания, вы можете получить тот же результат с помощью индексации. Есть много способов выполнить эту задачу, но эквивалентным решением является использование.locиндексатор иisin, инвертируя выделение.

df.loc[~df.index.isin([0,2]), ~df.columns.isin(['b', 'c'])]
    a   d   e
1   5   8   9
3  15  18  19
4  20  23  24

Если для вас ничего из этого не имеет смысла, я предлагаю вам прочитать мою серию о выборе и индексировании в пандах, начиная сздесьНачинать.

вернуться к вопросу

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

del df['a']
df
    b   c   d   e
0   1   2   3   4
1   6   7   8   9
2  11  12  13  14
3  16  17  18  19
4  21  22  23  24

Пуф, оно просто исчезло. Это как использоватьinplace=Trueудалить.

Как насчет доступа к собственности?

Мы также знаем, что можем использовать атрибут доступа к _select_DataFrameстолбец.

df.b
0     1
1     6
2    11
3    16
4    21
Name: b, dtype: int64

Можем ли мы удалить столбцы таким образом?

del df.b
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-10-0dca358a6ef9> in <module>
----> 1 del df.b

AttributeError: b

мы не можем. В текущем дизайне панд нельзя удалять столбцы. Это технически невозможно? Почемуdel df['b']Да, покаdel df.bНо это не работает? Давайте углубимся в эти детали и посмотрим, можно ли заставить работать и вторую версию.

Первая версия работает, потому что в пандахDataFrameДостигнуто__delitem__метод, когда вы выполняетеdel df['b']будет вызвано, когда . но,del df.b, есть ли способ справиться с этой проблемой?

Во-первых, давайте создадим простой класс, чтобы показать, как этот класс работает внутри. мы не делаем настоящегоDataFrame, но просто используйтеdict, как контейнер для нашего столбца (он действительно может содержать что угодно, мы не делаем здесь никакого индексирования).

class StupidFrame:
    def __init__(self, columns):
        self.columns = columns
        
    def __delitem__(self, item):
        del self.columns[item]
        
    def __getitem__(self, item):
        return self.columns[item]
    
    def __setitem__(self, item, val):
        self.columns[item] = val
            
f = StupidFrame({'a': 1, 'b': 2, 'c': 3})
print("StupidFrame value for a:", f['a'])
print("StupidFrame columns: ", f.columns)
del f['b']
f.d = 4
print("StupidFrame columns: ", f.columns)
StupidFrame value for a: 1
StupidFrame columns:  {'a': 1, 'b': 2, 'c': 3}
StupidFrame columns:  {'a': 1, 'c': 3}

Здесь следует отметить несколько вещей. Во-первых, мы можем использовать оператор индекса ([]) посетите нашStupidFrameи используйте его для установки, получения и удаления элементов. когда мы ставимdКогда он был назначен нашему фрейму, он не был добавлен в наш столбец, потому что это просто обычное свойство экземпляра. Если мы хотим иметь возможность обрабатывать столбцы как свойства, нам нужно проделать больше работы.

Итак, следуя примеру pandas (который поддерживает доступ к атрибутам для столбцов), мы добавляем__getattr__метод, но мы также будем использовать__setattr__Метод обрабатывает настройку и делает вид, что любое присвоение свойства является «столбцом». Мы должны обновить наш словарь экземпляров напрямую (__dict__), чтобы избежать бесконечной рекурсии.

class StupidFrameAttr:
    def __init__(self, columns):
        self.__dict__['columns'] = columns
        
    def __delitem__(self, item):
        del self.__dict__['columns'][item]
        
    def __getitem__(self, item):
        return self.__dict__['columns'][item]
    
    def __setitem__(self, item, val):
        self.__dict__['columns'][item] = val
        
    def __getattr__(self, item):
        if item in self.__dict__['columns']:
            return self.__dict__['columns'][item]
        elif item == 'columns':
            return self.__dict__[item]
        else:
            raise AttributeError
    
    def __setattr__(self, item, val):
        if item != 'columns':
            self.__dict__['columns'][item] = val
        else:
            raise ValueError("Overwriting columns prohibited") 

            
f = StupidFrameAttr({'a': 1, 'b': 2, 'c': 3})
print("StupidFrameAttr value for a", f['a'])
print("StupidFrameAttr columns: ", f.columns)
del f['b']
print("StupidFrameAttr columns: ", f.columns)
print("StupidFrameAttr value for a", f.a)
f.d = 4
print("StupidFrameAttr columns: ", f.columns)
del f['d']
print("StupidFrameAttr columns: ", f.columns)
f.d = 5
print("StupidFrameAttr columns: ", f.columns)
del f.d
StupidFrameAttr value for a 1
StupidFrameAttr columns:  {'a': 1, 'b': 2, 'c': 3}
StupidFrameAttr columns:  {'a': 1, 'c': 3}
StupidFrameAttr value for a 1
StupidFrameAttr columns:  {'a': 1, 'c': 3, 'd': 4}
StupidFrameAttr columns:  {'a': 1, 'c': 3}
StupidFrameAttr columns:  {'a': 1, 'c': 3, 'd': 5}
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-12-fd29f59ea01e> in <module>
     39 f.d = 5
     40 print("StupidFrameAttr columns: ", f.columns)
---> 41 del f.d

AttributeError: d

Как мы можем справиться с удалением?

Все работает, кроме удаления с использованием доступа к свойствам. Мы используем оператор индекса массива ([]) и доступ к свойствам для настройки/получения столбцов. Но как обнаружить удаление? Это возможно?

Один из способов - использовать__delattr__метод, которыймодель данныхОписано в документации. Если вы определите этот метод в своем классе, он будет вызываться вместо прямого обновления словаря свойств экземпляра. Это дает нам возможность перенаправить его в экземпляр нашего столбца.

class StupidFrameDelAttr(StupidFrameAttr):
    def __delattr__(self, item):
        # trivial implementation using the data model methods
        del self.__dict__['columns'][item]

f = StupidFrameDelAttr({'a': 1, 'b': 2, 'c': 3})
print("StupidFrameDelAttr value for a", f['a'])
print("StupidFrameDelAttr columns: ", f.columns)
del f['b']
print("StupidFrameDelAttr columns: ", f.columns)
print("StupidFrameDelAttr value for a", f.a)
f.d = 4
print("StupidFrameDelAttr columns: ", f.columns)
del f.d 
print("StupidFrameDelAttr columns: ", f.columns)
StupidFrameDelAttr value for a 1
StupidFrameDelAttr columns:  {'a': 1, 'b': 2, 'c': 3}
StupidFrameDelAttr columns:  {'a': 1, 'c': 3}
StupidFrameDelAttr value for a 1
StupidFrameDelAttr columns:  {'a': 1, 'c': 3, 'd': 4}
StupidFrameDelAttr columns:  {'a': 1, 'c': 3}

Теперь я не говорю, что удаление атрибутов столбцов легко добавить в pandas, но, по крайней мере, это показывает, что это возможно. В текущих пандах лучше всего использовать удаление столбцаdrop.

Кроме того, здесь стоит упомянуть, что когда вы создаете новый столбец в pandas, вы не назначаете его атрибутом. Чтобы лучше понять, как правильно создать столбец, вы можете посмотретьэта статья.

Если вы уже знаете, как сбросить столбец в pandas, надеюсь, вы узнали немного больше о работе.

The postHow to remove a column from a DataFrame, with some extra detailappeared first onwrighters.io.