Как удалить столбец, содержащий только нули в пандах?

87

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

ones = []
zeros = []
for year in years:
    for i in range(0,599):
        if year[str(i)].values.any() == 1:
            ones.append(i)
        if year[str(i)].values.all() == 0:
            zeros.append(i)
    for j in ones:
        if j in zeros:
            zeros.remove(j)
    for q in zeros:
        del year[str(q)]

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

user2587593
источник

Ответы:

214
df.loc[:, (df != 0).any(axis=0)]

Вот подробное описание того, как это работает:

In [74]: import pandas as pd

In [75]: df = pd.DataFrame([[1,0,0,0], [0,0,1,0]])

In [76]: df
Out[76]: 
   0  1  2  3
0  1  0  0  0
1  0  0  1  0

[2 rows x 4 columns]

df != 0создает логический DataFrame, который имеет значение True, где не dfравно нулю:

In [77]: df != 0
Out[77]: 
       0      1      2      3
0   True  False  False  False
1  False  False   True  False

[2 rows x 4 columns]

(df != 0).any(axis=0)возвращает логическую серию, указывающую, какие столбцы имеют ненулевые записи. ( anyОперация агрегирует значения по оси 0, то есть по строкам, в одно логическое значение. Следовательно, результатом является одно логическое значение для каждого столбца.)

In [78]: (df != 0).any(axis=0)
Out[78]: 
0     True
1    False
2     True
3    False
dtype: bool

И df.locможет использоваться для выбора этих столбцов:

In [79]: df.loc[:, (df != 0).any(axis=0)]
Out[79]: 
   0  2
0  1  0
1  0  1

[2 rows x 2 columns]

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

df = df.loc[:, (df != 0).any(axis=0)]
Unutbu
источник
Я пытаюсь удалить столбец, если в нем 0 или 1, и выдает ошибку: df = df.loc [:, (df! = 0 & df! = 1) .any (axis = 0)]
morpheus
1
df.loc[:, (~df.isin([0,1])).any(axis=0)]тоже будет работать.
unutbu 06
1
@IgorFobia: Многие вещи ложны, но не равны 0. Например, пустые строки или None или NaN. Для того, чтобы продемонстрировать разницу, если df = pd.DataFrame([[np.nan]*10]), то df.loc[:, df.any(axis=0)]возвращает пустую DataFrame, а df.loc[:, (df != 0).any(axis=0)]возвращает DataFrame с 10 колоннами.
unutbu
4
Я считаю, что легче понять, если мы проверяем, является ли условие истинным, вместо того, чтобы проверять, не выполняется ли условие, которое не является истинным, никогда. Я думаю, что (df == 0).all(axis=0)это проще.
Ryszard Cetnarski
2
Спасибо за поломку. Это прояснило ситуацию.
Regi Mathew
7

Вот альтернативный способ использования:

df.replace(0,np.nan).dropna(axis=1,how="all")

По сравнению с решением unutbu этот способ явно медленнее:

%timeit df.loc[:, (df != 0).any(axis=0)]
652 µs ± 5.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit df.replace(0,np.nan).dropna(axis=1,how="all")
1.75 ms ± 9.49 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Джереми Z
источник
0

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

zero_cols = [ col for col, is_zero in ((df == 0).sum() == df.shape[0]).items() if is_zero ]
df.drop(zero_cols, axis=1, inplace=True)

Некоторые ломаются:

# a pandas Series with {col: is_zero} items
# is_zero is True when the number of zero items in that column == num_all_rows
(df == 0).sum() == df.shape[0])

# a list comprehension of zero_col_names is built from the_series
[ col for col, is_zero in the_series.items() if is_zero ]
морк
источник