pandalar: operatör zinciriyle DataFrame satırlarını filtrele


329

En işlemleri pandasoperatör zincirleme (ile gerçekleştirilebilir groupby, aggregate, apply, vb), ama filtre satırları saptadığımız tek yolu, normal braket endeksleme yoluyladır

df_filtered = df[df['column'] == value]

dfDeğerlerini filtrelemeden önce bir değişkene atamamı gerektirdiğinden bu çekici değildir . Aşağıdaki gibi bir şey var mı?

df_filtered = df.mask(lambda x: x['column'] == value)

df.queryve pd.evalbu kullanım durumu için iyi uyuyor gibi görünüyor. pd.eval()İşlevler ailesi, özellikleri ve kullanım örnekleri hakkında bilgi için , lütfen pd.eval () kullanarak pandalarda Dinamik İfade Değerlendirme sayfasını ziyaret edin .
cs95

Yanıtlar:


384

Tamamen ne istediğinizden emin değilim ve son kod satırınız da yardımcı olmaz, ama yine de:

"Zincirleme" filtreleme, boole endeksindeki kriterlerin "zincirlenmesi" ile yapılır.

In [96]: df
Out[96]:
   A  B  C  D
a  1  4  9  1
b  4  5  0  2
c  5  5  1  0
d  1  3  9  6

In [99]: df[(df.A == 1) & (df.D == 6)]
Out[99]:
   A  B  C  D
d  1  3  9  6

Yöntemleri zincirlemek istiyorsanız, kendi maske yönteminizi ekleyebilir ve kullanabilirsiniz.

In [90]: def mask(df, key, value):
   ....:     return df[df[key] == value]
   ....:

In [92]: pandas.DataFrame.mask = mask

In [93]: df = pandas.DataFrame(np.random.randint(0, 10, (4,4)), index=list('abcd'), columns=list('ABCD'))

In [95]: df.ix['d','A'] = df.ix['a', 'A']

In [96]: df
Out[96]:
   A  B  C  D
a  1  4  9  1
b  4  5  0  2
c  5  5  1  0
d  1  3  9  6

In [97]: df.mask('A', 1)
Out[97]:
   A  B  C  D
a  1  4  9  1
d  1  3  9  6

In [98]: df.mask('A', 1).mask('D', 6)
Out[98]:
   A  B  C  D
d  1  3  9  6

2
Mükemmel cevap! Peki (df.A == 1) & (df.D == 6), panda'da "&" aşırı yüklü bir operatör mü?
Shawn


Bu gerçekten güzel bir çözüm - python'da böyle jüri-teçhizatı yapabileceğinizin farkında bile değildim. Böyle bir işlev Pandaların kendisinde olması gerçekten güzel olurdu.
naught101

Bununla ilgili tek sorun kullanımıdır pandas.. Yapmalısın import pandas as pd.
Daisuke Aramaki

3
Gerçekten import pandas as pdde yaygın bir uygulama. Soruyu cevapladığım zaman şüpheliyim.
Wouter Overmeire

108

Filtreler bir Panda sorgusu kullanılarak zincirlenebilir :

df = pd.DataFrame(np.random.randn(30, 3), columns=['a','b','c'])
df_filtered = df.query('a > 0').query('0 < b < 2')

Filtreler ayrıca tek bir sorguda birleştirilebilir:

df_filtered = df.query('a > 0 and 0 < b < 2')

3
Sorgunuzdaki python değişkenlerine başvurmanız gerekiyorsa, dokümantasyonda "@a + b gibi bir '@' karakteri ön ekleyerek ortamdaki değişkenlere başvurabilirsiniz" diyor. Aşağıdakilerin geçerli olduğuna dikkat edin: df.query('a in list([1,2])'), s = set([1,2]); df.query('a in @s').
user3780389

2
Öte yandan, sütun adınız belirli özel karakterlere sahipse sorgu değerlendirmesi başarısız olur: örn. "Yer.Adı".
user3780389

2
Zincirleme, sorgu için tasarlanmıştır.
piRSquared

66

@Lodagro'nun cevabı harika. Maske işlevini genelleştirerek genişletirim:

def mask(df, f):
  return df[f(df)]

Sonra aşağıdaki gibi şeyler yapabilirsiniz:

df.mask(lambda x: x[0] < 0).mask(lambda x: x[1] > 0)

8
Yararlı bir genelleme! Keşke doğrudan DataFrames içine entegre olsaydı !
duckworthd

24

Yana sürümü 0.18.1.loc yöntemi seçimi için çağrılabilir kabul eder. Lambda fonksiyonları ile birlikte çok esnek zincirlenebilir filtreler oluşturabilirsiniz:

import numpy as np
import pandas as pd

df = pd.DataFrame(np.random.randint(0,100,size=(100, 4)), columns=list('ABCD'))
df.loc[lambda df: df.A == 80]  # equivalent to df[df.A == 80] but chainable

df.sort_values('A').loc[lambda df: df.A > 80].loc[lambda df: df.B > df.A]

Yaptığınız tek şey filtrelemekse .loc,.


16

Bunu ek örnekler için öneriyorum. Bu, https://stackoverflow.com/a/28159296/ ile aynı cevaptır

Bu yayını daha kullanışlı hale getirmek için başka düzenlemeler ekleyeceğim.

pandas.DataFrame.query
querytam da bu amaçla yapıldı. Veri çerçevesini düşünündf

import pandas as pd
import numpy as np

np.random.seed([3,1415])
df = pd.DataFrame(
    np.random.randint(10, size=(10, 5)),
    columns=list('ABCDE')
)

df

   A  B  C  D  E
0  0  2  7  3  8
1  7  0  6  8  6
2  0  2  0  4  9
3  7  3  2  4  3
4  3  6  7  7  4
5  5  3  7  5  9
6  8  7  6  4  7
7  6  2  6  6  5
8  2  8  7  5  8
9  4  7  6  1  5

queryTüm satırları filtrelemek için kullanalım.D > B

df.query('D > B')

   A  B  C  D  E
0  0  2  7  3  8
1  7  0  6  8  6
2  0  2  0  4  9
3  7  3  2  4  3
4  3  6  7  7  4
5  5  3  7  5  9
7  6  2  6  6  5

Hangi zinciri

df.query('D > B').query('C > B')
# equivalent to
# df.query('D > B and C > B')
# but defeats the purpose of demonstrating chaining

   A  B  C  D  E
0  0  2  7  3  8
1  7  0  6  8  6
4  3  6  7  7  4
5  5  3  7  5  9
7  6  2  6  6  5

Bu temel olarak stackoverflow.com/a/28159296 ile aynı cevap değil mi Bu cevabın netleştirilmesi gerektiğini düşündüğünüz bir şey eksik mi?
bscan

9

Kriterleri bir OR koşulu ile birleştirmek istemem dışında aynı sorum vardı. Wouter Overmeire tarafından verilen format, kriterleri bir AND koşulunda birleştirir, böylece her ikisi de karşılanmalıdır:

In [96]: df
Out[96]:
   A  B  C  D
a  1  4  9  1
b  4  5  0  2
c  5  5  1  0
d  1  3  9  6

In [99]: df[(df.A == 1) & (df.D == 6)]
Out[99]:
   A  B  C  D
d  1  3  9  6

Ancak, her bir koşulu (... == True)sarır ve ölçütleri bir boru ile birleştirirseniz, ölçütlerin OR koşullarında birleştirildiğini ve her ikisinin de doğru olduğu durumlarda tatmin olduğunu buldum :

df[((df.A==1) == True) | ((df.D==6) == True)]

12
df[(df.A==1) | (df.D==6)]Yapmaya çalıştığınız şey için yeterli olmaz mı?
eenblam

Hayır, bunun yerine, durumu yerine getiren tüm verileri filtreleyen yukarıdaki yerine bollean sonuçları (True vs False) verdiği için olmaz. Umarım açıklığa kavuşturmuşumdur.
MGB.py

8

pandalar Wouter Overmeire'ın cevabını geçersiz kılma gerektirmeyen iki alternatif sunuyor. Biri olduğu .loc[.]gibi çağrılabilir

df_filtered = df.loc[lambda x: x['column'] == value]

diğeri .pipe(), olduğu gibi

df_filtered = df.pipe(lambda x: x['column'] == value)

7

Cevabım diğerlerine benzer. Yeni bir işlev oluşturmak istemiyorsanız, pandaların sizin için önceden tanımladığı şeyleri kullanabilirsiniz. Boru yöntemini kullanın.

df.pipe(lambda d: d[d['column'] == value])

Bu gibi komutları zincirlemek istiyorsanız istediğiniz şeydira.join(b).pipe(lambda df: df[df.column_to_filter == 'VALUE'])
displayname

4

Tüm genel boolean maskeleri ve genel amaçlı bir maskeyi uygulamak isterseniz, aşağıdakileri bir dosyada mandreleyebilir ve hepsini aşağıdaki gibi atayabilirsiniz:

pd.DataFrame = apply_masks()

Kullanımı:

A = pd.DataFrame(np.random.randn(4, 4), columns=["A", "B", "C", "D"])
A.le_mask("A", 0.7).ge_mask("B", 0.2)... (May be repeated as necessary

Biraz acayip ama veri kümelerini filtrelere göre sürekli kesip değiştiriyorsanız işleri biraz daha temiz hale getirebilir. Ayrıca, gen_mask fonksiyonunda yukarıdaki lambda fonksiyonlarıyla veya isterseniz başka bir şekilde kullanabileceğiniz Daniel Velkov'dan uyarlanmış genel amaçlı bir filtre de vardır.

Kaydedilecek dosya (masks.py kullanıyorum):

import pandas as pd

def eq_mask(df, key, value):
    return df[df[key] == value]

def ge_mask(df, key, value):
    return df[df[key] >= value]

def gt_mask(df, key, value):
    return df[df[key] > value]

def le_mask(df, key, value):
    return df[df[key] <= value]

def lt_mask(df, key, value):
    return df[df[key] < value]

def ne_mask(df, key, value):
    return df[df[key] != value]

def gen_mask(df, f):
    return df[f(df)]

def apply_masks():

    pd.DataFrame.eq_mask = eq_mask
    pd.DataFrame.ge_mask = ge_mask
    pd.DataFrame.gt_mask = gt_mask
    pd.DataFrame.le_mask = le_mask
    pd.DataFrame.lt_mask = lt_mask
    pd.DataFrame.ne_mask = ne_mask
    pd.DataFrame.gen_mask = gen_mask

    return pd.DataFrame

if __name__ == '__main__':
    pass

3

Bu çözüm, uygulama açısından daha kırılgandır, ancak kullanım açısından çok daha temiz buluyorum ve kesinlikle diğerlerinden daha genel.

https://github.com/toobaz/generic_utils/blob/master/generic_utils/pandas/where.py

Reponun tamamını indirmenize gerek yok: dosyayı kaydetme ve

from where import where as W

yeterli olmalı. Sonra böyle kullanın:

df = pd.DataFrame([[1, 2, True],
                   [3, 4, False], 
                   [5, 7, True]],
                  index=range(3), columns=['a', 'b', 'c'])
# On specific column:
print(df.loc[W['a'] > 2])
print(df.loc[-W['a'] == W['b']])
print(df.loc[~W['c']])
# On entire - or subset of a - DataFrame:
print(df.loc[W.sum(axis=1) > 3])
print(df.loc[W[['a', 'b']].diff(axis=1)['b'] > 1])

Biraz daha az aptal kullanım örneği:

data = pd.read_csv('ugly_db.csv').loc[~(W == '$null$').any(axis=1)]

Bu arada: sadece boole kömürleri kullandığınız durumda bile,

df.loc[W['cond1']].loc[W['cond2']]

çok daha verimli olabilir

df.loc[W['cond1'] & W['cond2']]

o değerlendirir çünkü cond2sadece nerede cond1olduğunu True.

YASAL UYARI: Bu cevabı önce başka bir yerde verdim çünkü görmedim.


2

Sadece kullanarak bir demo eklemek istiyorum loc satırlara göre değil, sütunlara ve zincirleme işlemin bazı değerlerine göre filtrelemek için .

Aşağıdaki kod, satırları değere göre filtreleyebilir.

df_filtered = df.loc[df['column'] == value]

Biraz değiştirerek sütunları da filtreleyebilirsiniz.

df_filtered = df.loc[df['column'] == value, ['year', 'column']]

Öyleyse neden zincirleme bir yöntem istiyoruz? Cevap, çok fazla işleminiz varsa okumanın basit olmasıdır. Örneğin,

res =  df\
    .loc[df['station']=='USA', ['TEMP', 'RF']]\
    .groupby('year')\
    .agg(np.nanmean)

2

Atamamı gerektirdiğinden bu çekici değil dfDeğerlerini filtrelemeden önce bir değişkene .

df[df["column_name"] != 5].groupby("other_column_name")

işe yarıyor gibi görünüyor: []operatörü de yuvalayabilirsiniz . Belki soruyu sorduğunuzdan beri eklediler.


1
Bu, bir zincirde çok az mantıklıdır, çünkü dfşimdi, zincirin previour kısmının çıktısına başvurmak zorunda değildir.
Daan Luttik

@ DaLLtik: kabul etti, zincirleme değil, yuvalama. Senin için daha iyi?
serv-inc

1

Sütunlarınızı dizin olarak arama yapacak şekilde ayarlarsanız, DataFrame.xs()bir kesit almak için kullanabilirsiniz . Bu, querycevaplar kadar çok yönlü değildir , ancak bazı durumlarda yararlı olabilir.

import pandas as pd
import numpy as np

np.random.seed([3,1415])
df = pd.DataFrame(
    np.random.randint(3, size=(10, 5)),
    columns=list('ABCDE')
)

df
# Out[55]: 
#    A  B  C  D  E
# 0  0  2  2  2  2
# 1  1  1  2  0  2
# 2  0  2  0  0  2
# 3  0  2  2  0  1
# 4  0  1  1  2  0
# 5  0  0  0  1  2
# 6  1  0  1  1  1
# 7  0  0  2  0  2
# 8  2  2  2  2  2
# 9  1  2  0  2  1

df.set_index(['A', 'D']).xs([0, 2]).reset_index()
# Out[57]: 
#    A  D  B  C  E
# 0  0  2  2  2  2
# 1  0  2  1  1  0

1

Mantıksal işlemler için numpy kütüphanesinden de yararlanabilirsiniz . Oldukça hızlı.

df[np.logical_and(df['A'] == 1 ,df['B'] == 6)]
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.