पांडा: ऑपरेटर चाइनिंग के साथ डेटाफ़्रेम की पंक्तियों को फ़िल्टर करें


329

में अधिकांश संचालन pandasऑपरेटर श्रृंखलन (साथ पूरा किया जा सकता groupby, aggregate, apply, आदि), लेकिन एक ही रास्ता मैं फिल्टर पंक्तियों को मिल गया है सामान्य ब्रैकेट अनुक्रमण के माध्यम से है

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

यह अनुपयोगी है क्योंकि dfइसके मानों को फ़िल्टर करने में सक्षम होने से पहले मुझे एक चर पर असाइन करने की आवश्यकता है । क्या निम्नलिखित की तरह कुछ और है?

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

df.queryऔर pd.evalइस उपयोग के मामले के लिए अच्छे फिट्स लगते हैं। pd.eval()फ़ंक्शंस के परिवार, उनकी विशेषताओं और उपयोग के मामलों की जानकारी के लिए, कृपया pd.eval () का उपयोग करके पांडा में डायनेमिक एक्सप्रेशन मूल्यांकन का दौरा करें
cs95

जवाबों:


384

मुझे पूरी तरह से यकीन नहीं है कि आप क्या चाहते हैं, और आपकी अंतिम पंक्ति कोड या तो मदद नहीं करती है, लेकिन वैसे भी:

"जंजीर" फ़िल्टरिंग बूलियन इंडेक्स में मानदंड "चाइनिंग" द्वारा की जाती है।

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

यदि आप श्रृंखला विधियों को चाहते हैं, तो आप अपनी स्वयं की मास्क विधि जोड़ सकते हैं और उस एक का उपयोग कर सकते हैं।

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
बहुत बढ़िया जवाब! तो (df.A == 1) & (df.D == 6), पंडों में "और" एक अतिभारित ऑपरेटर है?
शॉन

2
वास्तव में, यह भी देखें pandas.pydata.org/pandas-docs/stable/…
Wouter Overmeire

यह एक बहुत अच्छा समाधान है - मुझे यह भी पता नहीं था कि आप अजगर की तरह जूरी-रिग के तरीकों को अपना सकते हैं। इस तरह का एक समारोह पंडों में ही होना बहुत अच्छा होगा।
n

एकमात्र समस्या जो मुझे इसके साथ है, उसका उपयोग है pandas.। आपको चाहिए import pandas as pd
Daisuke Aramaki

3
दरअसल import pandas as pdयह आम बात है। मुझे संदेह है कि जब मैंने सवाल का जवाब दिया था।
राउटर ओवरमेइरे

108

पंडों की क्वेरी का उपयोग करके फिल्टर को जंजीर बनाया जा सकता है :

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

फिल्टर भी एक ही क्वेरी में जोड़ा जा सकता है:

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

3
यदि आपको अपनी क्वेरी में अजगर वेरिएबल्स को संदर्भित करने की आवश्यकता है, तो दस्तावेज कहता है, "आप वातावरण में चर को '@' वर्ण जैसे @ @ 'के साथ उपसर्ग करके संदर्भित कर सकते हैं।" ध्यान दें कि निम्नलिखित मान्य हैं: df.query('a in list([1,2])'), s = set([1,2]); df.query('a in @s')
user3780389

2
दूसरी ओर, ऐसा लगता है कि यदि आपके स्तंभ नाम में कुछ विशेष वर्ण हैं: जैसे "Place.Name", तो क्वेरी मूल्यांकन विफल हो जाएगा।
user3780389

2
चेनिंग किस क्वेरी के लिए डिज़ाइन किया गया है।
piRSquared

66

@Lodagro से जवाब बहुत अच्छा है। मैं मास्क फ़ंक्शन को सामान्य करके इसे बढ़ाऊंगा:

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

फिर आप सामान की तरह कर सकते हैं:

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

8
एक उपयोगी सामान्यीकरण! काश यह सीधे DataFrameपहले से ही एकीकृत होता !
डकवर्थ

24

संस्करण 0.18.1 के बाद से .locविधि चयन के लिए एक कॉल करने योग्य स्वीकार करती है। लैम्ब्डा फंक्शंस के साथ मिलकर आप बहुत लचीले चेनेबल फिल्टर बना सकते हैं:

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]

यदि आप सब कर रहे हैं फ़िल्टरिंग, आप भी छोड़ सकते हैं .loc


16

मैं इसे अतिरिक्त उदाहरणों के लिए प्रस्तुत करता हूं। यह https://stackoverflow.com/a/28159296/ के समान उत्तर है

मैं इस पोस्ट को और उपयोगी बनाने के लिए अन्य संपादन जोड़ूंगा।

pandas.DataFrame.query
queryइस उद्देश्य के लिए बनाया गया था। डेटाफ्रेम पर विचार करेंdf

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

queryजहां सभी पंक्तियों को फ़िल्टर करने के लिए उपयोग करें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

जिसे हम चेन करते हैं

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

क्या यह मूल रूप से stackoverflow.com/a/28159296 के समान उत्तर नहीं है ? क्या उस उत्तर से कुछ गायब है जो आपको लगता है कि स्पष्ट किया जाना चाहिए?
bscan

9

मेरे पास एक ही सवाल था, सिवाय इसके कि मैं मानदंडों को एक OR स्थिति में संयोजित करना चाहता था। Wouter Overmeire द्वारा दिया गया प्रारूप मानदंड को एक ऐसी स्थिति में जोड़ता है कि दोनों को संतुष्ट होना चाहिए:

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

लेकिन मैंने पाया कि, यदि आप प्रत्येक शर्त को लपेटते हैं (... == True)और एक पाइप के साथ मापदंड में शामिल होते हैं, तो मानदंड को एक OR स्थिति में संयोजित किया जाता है, जब भी उनमें से कोई भी सत्य हो तो संतुष्ट:

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

12
df[(df.A==1) | (df.D==6)]क्या आप पूरा करने की कोशिश कर रहे हैं के लिए पर्याप्त नहीं होगा ?
इन्नबलम

नहीं, यह नहीं होगा क्योंकि यह बोलियन परिणाम देता है (ट्रू बनाम फाल्स) इसके बजाय यह ऊपर है जो सभी डेटा को फ़िल्टर करता है जो स्थिति को संतुष्ट करते हैं। आशा है कि मैंने इसे स्पष्ट किया।
9GB पर MGB.py

8

पांडा ओवररेट के उत्तर के लिए दो विकल्प प्रदान करता है जिन्हें किसी भी ओवरराइडिंग की आवश्यकता नहीं होती है। एक .loc[.]कॉल करने योग्य है, जैसा कि अंदर है

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

दूसरा है .pipe(), जैसा कि अंदर है

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

7

मेरा जवाब दूसरों के समान है। यदि आप एक नया फ़ंक्शन नहीं बनाना चाहते हैं, तो आप पहले से ही आपके लिए जो पांडा निर्धारित कर चुके हैं, उसका उपयोग कर सकते हैं। पाइप विधि का उपयोग करें।

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

इस क्या आप इस तरह के रूप में चाहते हैं, तो आप श्रृंखला आदेशों करना चाहते हैa.join(b).pipe(lambda df: df[df.column_to_filter == 'VALUE'])
DisplayName

4

यदि आप सभी सामान्य बूलियन मास्क के साथ-साथ एक सामान्य उद्देश्य मुखौटा लागू करना चाहते हैं, तो आप एक फ़ाइल में निम्नलिखित को चक सकते हैं और फिर उन्हें निम्नानुसार असाइन कर सकते हैं:

pd.DataFrame = apply_masks()

उपयोग:

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

यह थोड़ा सा हैक है, लेकिन अगर आप लगातार चॉप कर रहे हैं और फ़िल्टर के अनुसार डेटासेट बदल रहे हैं तो यह थोड़ा साफ कर सकता है। डैनियल वेलकोव से ऊपर एक सामान्य प्रयोजन फिल्टर भी है जो कि जीनमैक्स फंक्शन में है जिसे आप लैम्ब्डा फ़ंक्शन के साथ उपयोग कर सकते हैं या अन्यथा वांछित।

सहेजी जाने वाली फ़ाइल (मैं मास्क का उपयोग करता हूं):

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

कार्यान्वयन के संदर्भ में यह समाधान अधिक हैकिश है, लेकिन मैं इसे उपयोग के मामले में बहुत अधिक स्वच्छ पाता हूं, और यह निश्चित रूप से प्रस्तावित अन्यों की तुलना में अधिक सामान्य है।

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

आपको संपूर्ण रेपो डाउनलोड करने की आवश्यकता नहीं है: फ़ाइल को सहेजना और करना

from where import where as W

पर्याप्त होना चाहिए। तो आप इसे इस तरह से उपयोग करें:

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

थोड़ा कम बेवकूफ उपयोग उदाहरण:

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

वैसे: यहां तक ​​कि जिस मामले में आप बूलियन कॉल का उपयोग कर रहे हैं,

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

की तुलना में बहुत अधिक कुशल हो सकता है

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

क्योंकि यह cond2केवल जहां cond1का मूल्यांकन करता है True

अस्वीकरण: मैंने पहली बार यह जवाब कहीं और दिया क्योंकि मैंने इसे नहीं देखा था।


2

locन केवल पंक्तियों के आधार पर, बल्कि स्तंभों द्वारा और फ़िल्टर किए गए ऑपरेशन के लिए कुछ गुणों को फ़िल्टर करके एक प्रदर्शन जोड़ना चाहते हैं ।

नीचे दिए गए कोड मूल्य द्वारा पंक्तियों को फ़िल्टर कर सकते हैं।

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

इसे थोड़ा संशोधित करके आप कॉलम को भी फ़िल्टर कर सकते हैं।

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

तो हम एक जंजीर पद्धति क्यों चाहते हैं? इसका उत्तर यह है कि यदि आपके पास कई ऑपरेशन हैं तो यह पढ़ना सरल है। उदाहरण के लिए,

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

2

यह अनुपयोगी है क्योंकि dfइसके मानों को फ़िल्टर करने में सक्षम होने से पहले मुझे एक चर पर असाइन करने की आवश्यकता है ।

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

काम करने लगता है: आप []ऑपरेटर को भी घोंसला बना सकते हैं । हो सकता है कि सवाल पूछने के बाद उन्होंने इसे जोड़ा हो।


1
यह एक श्रृंखला में बहुत कम समझ में आता है क्योंकि dfअब आवश्यक रूप से ते श्रृंखला के पूर्ववर्ती भाग के उत्पादन को संदर्भित नहीं करता है।
दान लुट्टिक

@DanLuttik: सहमत है, यह पीछा नहीं है, लेकिन घोंसले के शिकार है। आपके लिए बेहतर?
सर्व-इन

1

यदि आप अपने कॉलम को अनुक्रमित के रूप में खोजने के लिए सेट करते हैं, तो आप DataFrame.xs()क्रॉस सेक्शन लेने के लिए उपयोग कर सकते हैं । यह queryउत्तर के रूप में बहुमुखी नहीं है, लेकिन यह कुछ स्थितियों में उपयोगी हो सकता है।

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

आप लॉजिकल ऑपरेशंस के लिए सुपी लाइब्रेरी का भी लाभ उठा सकते हैं । यह बहुत तेज है।

df[np.logical_and(df['A'] == 1 ,df['B'] == 6)]
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.