Upuść wiersze ze wszystkimi zerami w ramce danych pandy

107

Mogę użyć pandas dropna()funkcji, aby usunąć wiersze z niektórymi lub wszystkimi kolumnami ustawionymi jako NA's. Czy istnieje równoważna funkcja do usuwania wierszy ze wszystkimi kolumnami o wartości 0?

P   kt  b   tt  mky depth
1   0   0   0   0   0
2   0   0   0   0   0
3   0   0   0   0   0
4   0   0   0   0   0
5   1.1 3   4.5 2.3 9.0

W tym przykładzie chcielibyśmy usunąć pierwsze 4 wiersze z ramki danych.

dzięki!

user308827
źródło
Dla wyjaśnienia, oto dwa pytania. Po pierwsze, aby usunąć kolumny ze wszystkimi wartościami równymi 0. Ale także dla funkcji równoważnej dropna (), która usuwałaby kolumny z dowolną wartością równą 0.
alchemia

Odpowiedzi:

117

Okazuje się, że można to ładnie wyrazić w sposób zwektoryzowany:

> df = pd.DataFrame({'a':[0,0,1,1], 'b':[0,1,0,1]})
> df = df[(df.T != 0).any()]
> df
   a  b
1  0  1
2  1  0
3  1  1
U2EF1
źródło
7
Fajnie, ale myślę, że można uniknąć negacji zdf = df[(df.T != 0).any()]
Akavall
1
@Akavall Dużo lepiej!
U2EF1
1
Tylko uwaga: OP chciał upuścić rows with all columns having value 0, ale można wywnioskować allmetodę.
paulochf
1
Wszystkie te odpowiedzi wyjaśniają, jak możemy usunąć wiersze ze wszystkimi zerami, jednak chciałem usunąć wiersze z 0 w pierwszej kolumnie. Z pomocą wszystkich dyskusji i odpowiedzi w tym poście zrobiłem to, wykonując df.loc [df.iloc [:, 0]! = 0]. Chciałem się tylko udostępnić, ponieważ ten problem jest związany z tym pytaniem !!
hemanta,
2
Transpozycja nie jest konieczna, any () może przyjąć oś jako parametr. Więc to działa: df = df [df.any (axis = 1)]
Rahul Jha,
134

Jednowierszowy. Nie potrzeba transpozycji:

df.loc[~(df==0).all(axis=1)]

A dla tych, którzy lubią symetrię, działa to również ...

df.loc[(df!=0).any(axis=1)]
8one6
źródło
1
Dla zwięzłości (i, moim zdaniem, jasność celów) i połączyć ten komentarz Akavall za: df.loc[(df != 0).any(1)]. Praca zespołowa!
Dan Allan,
1
+1, 30% szybciej niż transpozycja - od 491 do 614 mikrosekund i lubię to, że axis=1jest wyraźna; bardziej pythonowy moim zdaniem
gt6989b
Należy wspomnieć o różnicy między używaniem .all i .any, ponieważ pierwotne pytanie dotyczyło równoważności dropna. Jeśli chcesz usunąć wszystkie wiersze z dowolną kolumną zawierającą zero, musisz odwrócić .all i .any w powyższej odpowiedzi. Zajęło mi trochę czasu, zanim zdałem sobie z tego sprawę, ponieważ szukałem tej funkcjonalności.
Zak Keirn
To nie działa dla mnie, ale zwraca mi dokładnie to samodf
Robvh
Czy istnieje wersja „inplace” tego? Widzę, że aby upuścić wiersze w df zgodnie z żądaniem OP, musiałoby to być df = df.loc[(df!=0).all(axis=1)]i df = df.loc[(df!=0).any(axis=1)]usunąć wiersze z zerami, tak jak byłby rzeczywisty odpowiednik dropna ().
alchemia
20

Sprawdzam to pytanie mniej więcej raz w miesiącu i zawsze muszę wyciągać najlepszą odpowiedź z komentarzy:

df.loc[(df!=0).any(1)]

Dzięki Dan Allan!

The Unfun Cat
źródło
2
Nie wymaga kopania. @ 8one6 zawarł to w swojej odpowiedzi w samym 2014 roku, w części, która mówi: „A dla tych, którzy lubią symetrię ...”.
Rahul Murmuria
15

Zamień zera na, nana następnie upuść wiersze ze wszystkimi wpisami jako nan. Następnie zamień na nanzera.

import numpy as np
df = df.replace(0, np.nan)
df = df.dropna(how='all', axis=0)
df = df.replace(np.nan, 0)
stackpopped
źródło
5
To się nie powiedzie, jeśli masz jakieś istniejące wcześniej NaN-y w danych.
OmerB
13

Myślę, że to rozwiązanie jest najkrótsze:

df= df[df['ColName'] != 0]
ikbel benab
źródło
2
I jest na miejscu!
Max Kleiner
1
@MaxKleiner inplace z powodu ponownego przypisania zmiennej
lukas
7

Kilka rozwiązań, które okazały się pomocne, szukając tego, szczególnie w przypadku większych zestawów danych:

df[(df.sum(axis=1) != 0)]       # 30% faster 
df[df.values.sum(axis=1) != 0]  # 3X faster 

Kontynuując przykład z @ U2EF1:

In [88]: df = pd.DataFrame({'a':[0,0,1,1], 'b':[0,1,0,1]})

In [91]: %timeit df[(df.T != 0).any()]
1000 loops, best of 3: 686 µs per loop

In [92]: df[(df.sum(axis=1) != 0)]
Out[92]: 
   a  b
1  0  1
2  1  0
3  1  1

In [95]: %timeit df[(df.sum(axis=1) != 0)]
1000 loops, best of 3: 495 µs per loop

In [96]: %timeit df[df.values.sum(axis=1) != 0]
1000 loops, best of 3: 217 µs per loop

W przypadku większego zbioru danych:

In [119]: bdf = pd.DataFrame(np.random.randint(0,2,size=(10000,4)))

In [120]: %timeit bdf[(bdf.T != 0).any()]
1000 loops, best of 3: 1.63 ms per loop

In [121]: %timeit bdf[(bdf.sum(axis=1) != 0)]
1000 loops, best of 3: 1.09 ms per loop

In [122]: %timeit bdf[bdf.values.sum(axis=1) != 0]
1000 loops, best of 3: 517 µs per loop
zegar
źródło
Czy dzieje się coś złego, jeśli w wierszu znajduje się -1 i 1?
Rhys Ulerich
Oczywiście suma nie zadziałałaby, gdybyś miał równe wiersze, które sumują się do 0. Oto szybkie obejście tego, które jest tylko trochę wolniejsze: df[~(df.values.prod(axis=1) == 0) | ~(df.values.sum(axis=1)==0)]
zegar
Funkcja prod () niczego nie rozwiązuje. Jeśli masz dowolne 0 w wierszu, które zwróci 0. Jeśli musisz obsłużyć taki wiersz: [-1, -0,5, 0, 0,5, 1], żadne z rozwiązań nie zadziała.
Rahul Murmuria
Oto poprawna wersja, która działa 3x szybciej niż zaakceptowana odpowiedź: bdf[np.square(bdf.values).sum(axis=1) != 0]
Rahul Murmuria
5
import pandas as pd

df = pd.DataFrame({'a' : [0,0,1], 'b' : [0,0,-1]})

temp = df.abs().sum(axis=1) == 0      
df = df.drop(temp)

Wynik:

>>> df
   a  b
2  1 -1
Akavall
źródło
Nie działa dla mnie z 1-kolumnową ramką danych. GotValueError: labels [True ... ] not contained in matrix
The Unfun Cat
zamiast df = df.drop(temp)używaćdf = df.drop(df[temp].index)
Douglas Ferreira
3

Możesz użyć szybkiej lambdafunkcji, aby sprawdzić, czy wszystkie wartości w danym wierszu są 0. Następnie możesz użyć wyniku zastosowania tego lambdajako sposobu na wybranie tylko wierszy, które pasują lub nie spełniają tego warunku:

import pandas as pd
import numpy as np

np.random.seed(0)

df = pd.DataFrame(np.random.randn(5,3), 
                  index=['one', 'two', 'three', 'four', 'five'],
                  columns=list('abc'))

df.loc[['one', 'three']] = 0

print df
print df.loc[~df.apply(lambda row: (row==0).all(), axis=1)]

Plony:

              a         b         c
one    0.000000  0.000000  0.000000
two    2.240893  1.867558 -0.977278
three  0.000000  0.000000  0.000000
four   0.410599  0.144044  1.454274
five   0.761038  0.121675  0.443863

[5 rows x 3 columns]
             a         b         c
two   2.240893  1.867558 -0.977278
four  0.410599  0.144044  1.454274
five  0.761038  0.121675  0.443863

[3 rows x 3 columns]
8one6
źródło
1

Inna alternatywa:

# Is there anything in this row non-zero?
# df != 0 --> which entries are non-zero? T/F
# (df != 0).any(axis=1) --> are there 'any' entries non-zero row-wise? T/F of rows that return true to this statement.
# df.loc[all_zero_mask,:] --> mask your rows to only show the rows which contained a non-zero entry.
# df.shape to confirm a subset.

all_zero_mask=(df != 0).any(axis=1) # Is there anything in this row non-zero?
df.loc[all_zero_mask,:].shape
bmc
źródło
1

to działa dla mnie new_df = df[df.loc[:]!=0].dropna()

majdoul jihane
źródło
1
proszę używać markdown w swoich odpowiedziach
Ironkey
0

U mnie ten kod: df.loc[(df!=0).any(axis=0)] nie działał. Zwrócił dokładny zestaw danych.

Zamiast tego użyłem df.loc[:, (df!=0).any(axis=0)]i usunąłem wszystkie kolumny z wartościami 0 w zestawie danych

Funkcja .all()usunęła wszystkie kolumny, w których znajdują się jakiekolwiek wartości zerowe w moim zbiorze danych.

Denisa
źródło
-1
df = df [~( df [ ['kt'  'b'   'tt'  'mky' 'depth', ] ] == 0).all(axis=1) ]

Wypróbuj to polecenie, które działa doskonale.

Kumar Prasanna
źródło
-2

Aby usunąć wszystkie kolumny z wartościami 0 w dowolnym wierszu:

new_df = df[df.loc[:]!=0].dropna()
Yapi
źródło