पंडों के लिए कई फिल्टर लगाने का कुशल तरीका DataFrame या Series


148

मेरे पास एक परिदृश्य है जहां एक उपयोगकर्ता पंडों डेटाफ़्रेम या श्रृंखला ऑब्जेक्ट के लिए कई फ़िल्टर लागू करना चाहता है। अनिवार्य रूप से, मैं कुशलतापूर्वक फ़िल्टरिंग (तुलना संचालन) का एक गुच्छा चेन करना चाहता हूं जो उपयोगकर्ता द्वारा रन-टाइम पर निर्दिष्ट किए गए हों।

फिल्टर एडिटिव (उर्फ प्रत्येक को लागू किया जाना चाहिए संकीर्ण परिणाम) होना चाहिए।

मैं वर्तमान में उपयोग कर रहा हूं reindex()लेकिन यह हर बार एक नई वस्तु बनाता है और अंतर्निहित डेटा की प्रतिलिपि बनाता है (यदि मैं प्रलेखन को सही ढंग से समझता हूं)। इसलिए, किसी बड़ी श्रृंखला या डेटाफ़्रेम को फ़िल्टर करते समय यह वास्तव में अक्षम हो सकता है।

मैं सोच रहा हूँ कि का उपयोग करते हुए apply(), map(), या ऐसा ही कुछ बेहतर हो सकता है। मैं पंडों के लिए बहुत नया हूं, हालांकि अभी भी मेरे सिर को हर चीज के चारों ओर लपेटने की कोशिश कर रहा हूं।

टी एल; डॉ

मैं निम्नलिखित फॉर्म का शब्दकोश लेना चाहता हूं और दिए गए सीरीज ऑब्जेक्ट में प्रत्येक ऑपरेशन को लागू करता हूं और एक 'फिल्टर्ड' सीरीज ऑब्जेक्ट को वापस करता हूं।

relops = {'>=': [1], '<=': [1]}

लंबा उदाहरण

मैं इस बात का उदाहरण देता हूं कि मेरे पास वर्तमान में क्या है और केवल एक ही श्रृंखला ऑब्जेक्ट को फ़िल्टर कर रहा हूं। नीचे वह फ़ंक्शन है जिसका मैं वर्तमान में उपयोग कर रहा हूं:

   def apply_relops(series, relops):
        """
        Pass dictionary of relational operators to perform on given series object
        """
        for op, vals in relops.iteritems():
            op_func = ops[op]
            for val in vals:
                filtered = op_func(series, val)
                series = series.reindex(series[filtered])
        return series

उपयोगकर्ता उन कार्यों के साथ एक शब्दकोश प्रदान करता है जिसे वे करना चाहते हैं:

>>> df = pandas.DataFrame({'col1': [0, 1, 2], 'col2': [10, 11, 12]})
>>> print df
>>> print df
   col1  col2
0     0    10
1     1    11
2     2    12

>>> from operator import le, ge
>>> ops ={'>=': ge, '<=': le}
>>> apply_relops(df['col1'], {'>=': [1]})
col1
1       1
2       2
Name: col1
>>> apply_relops(df['col1'], relops = {'>=': [1], '<=': [1]})
col1
1       1
Name: col1

फिर, मेरे उपरोक्त दृष्टिकोण के साथ 'समस्या' यह है कि मुझे लगता है कि इन-द-स्टेप के लिए डेटा की अनावश्यक रूप से बहुत अधिक नकल है।

इसके अलावा, मैं इसका विस्तार करना चाहूंगा ताकि पारित किए गए शब्दकोश में कॉलम को ऑपरेटर में शामिल किया जा सके और इनपुट शब्दकोश के आधार पर संपूर्ण DataFrame को फ़िल्टर किया जा सके। हालाँकि, मैं यह मान रहा हूं कि श्रृंखला के लिए जो भी काम करता है उसे आसानी से एक DataFrame तक विस्तारित किया जा सकता है।


इसके अलावा, मैं पूरी तरह से अवगत हूं कि समस्या के लिए यह तरीका बंद हो सकता है। तो शायद पूरे दृष्टिकोण को पुनर्विचार करना उपयोगी होगा। मैं केवल उपयोगकर्ताओं को रनटाइम पर फ़िल्टर संचालन का एक सेट निर्दिष्ट करने और उन्हें निष्पादित करने की अनुमति देना चाहता हूं।
ड्यूरडेन 2.0

मुझे आश्चर्य हो रहा है कि अगर पंडास डेटा के समान काम कर सकते हैं। R: df [col1 <1 ,,] [col2> = 1]
xappppp

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

जवाबों:


245

पंडों (और सुन्न) बूलियन अनुक्रमण के लिए अनुमति देते हैं , जो बहुत अधिक कुशल होगा:

In [11]: df.loc[df['col1'] >= 1, 'col1']
Out[11]: 
1    1
2    2
Name: col1

In [12]: df[df['col1'] >= 1]
Out[12]: 
   col1  col2
1     1    11
2     2    12

In [13]: df[(df['col1'] >= 1) & (df['col1'] <=1 )]
Out[13]: 
   col1  col2
1     1    11

यदि आप इसके लिए सहायक कार्य लिखना चाहते हैं, तो इन पंक्तियों पर विचार करें:

In [14]: def b(x, col, op, n): 
             return op(x[col],n)

In [15]: def f(x, *b):
             return x[(np.logical_and(*b))]

In [16]: b1 = b(df, 'col1', ge, 1)

In [17]: b2 = b(df, 'col1', le, 1)

In [18]: f(df, b1, b2)
Out[18]: 
   col1  col2
1     1    11

अद्यतन: पांडा 0.13 में इस तरह के उपयोग के मामलों के लिए एक क्वेरी पद्धति है , मान लिया गया है कि स्तंभ के नाम मान्य पहचानकर्ता निम्न कार्य हैं (और बड़े फ़्रेमों के लिए अधिक कुशल हो सकते हैं क्योंकि यह पर्दे के पीछे सुन्नता का उपयोग करता है ):

In [21]: df.query('col1 <= 1 & 1 <= col1')
Out[21]:
   col1  col2
1     1    11

1
आपका अधिकार, बूलियन अधिक कुशल है क्योंकि यह डेटा की प्रतिलिपि नहीं बनाता है। हालाँकि, मेरा परिदृश्य आपके उदाहरण से थोड़ा अधिक कठिन है। मुझे जो इनपुट मिलता है, वह एक डिक्शनरी है, जिसे फ़िल्टर करना है। मेरा उदाहरण कुछ ऐसा कर सकता है df[(ge(df['col1'], 1) & le(df['col1'], 1)]। मेरे लिए मुद्दा वास्तव में फिल्टर के साथ शब्दकोष है जिसमें बहुत सारे ऑपरेटर हो सकते हैं और उन्हें एक साथ जोड़ना बोझिल है। शायद मैं प्रत्येक मध्यवर्ती बूलियन सरणी को एक बड़े सरणी में जोड़ सकता हूं और फिर बस ऑपरेटर को उन्हें mapलागू करने के लिए उपयोग andकर सकता हूं?
दुरदेन 2.0

@ durden2.0 मैंने एक सहायक कार्य के लिए एक विचार जोड़ा है, जो मुझे लगता है कि आप जो देख रहे हैं, उसके समान है :)
एंडी हेडन

यह बहुत करीब लग रहा है कि मैं क्या लेकर आया हूँ! उदाहरण के लिए धन्यवाद। बस के बजाय f()लेने की आवश्यकता क्यों है ? क्या यह उपयोगकर्ता अभी भी वैकल्पिक पैरामीटर का उपयोग कर सकता है ? इससे एक और छोटा-सा सवाल सामने आता है। सरणी में पास होने के माध्यम से प्रदर्शन लाभ / व्यापार बंद हो जाता है । एक बार फिर धन्यवाद! *bbf()outlogical_and()out()logical_and()
ड्यूरडेन 2.0

कोई बात नहीं, मैं काफी करीब नहीं लग रहा था। *bक्योंकि आप दो सरणियों से गुजर रहे हैं के लिए आवश्यक है b1और b2और आप जब बुला उन्हें खोल करने की जरूरत है logical_and। हालांकि, दूसरा सवाल अभी भी खड़ा है। क्या outपैरामीटर के माध्यम से एक सरणी में पास करने के लिए एक प्रदर्शन लाभ है logical_and()बनाम सिर्फ अपने 'वापसी मूल्य का उपयोग करने के लिए ?
ड्यूरडेन2.0

2
@dwanderson आप कई स्थितियों के लिए np.logical_and.reduce के लिए शर्तों की एक सूची पास कर सकते हैं। उदाहरण: np.logical_and.reduce ([df ['a'] == 3, df ['b']> १०, df ['c ’]।
Isin

39

जंजीर की स्थिति लंबी लाइनें बनाती है, जो pep8 द्वारा हतोत्साहित की जाती हैं। .Query मेथड का उपयोग स्ट्रिंग्स का उपयोग करने के लिए करता है, जो कि शक्तिशाली है, लेकिन unpythonic और बहुत गतिशील नहीं है।

एक बार फिल्टर के प्रत्येक स्थान पर होने के बाद, एक दृष्टिकोण होता है

import numpy as np
import functools
def conjunction(*conditions):
    return functools.reduce(np.logical_and, conditions)

c_1 = data.col1 == True
c_2 = data.col2 < 64
c_3 = data.col3 != 4

data_filtered = data[conjunction(c1,c2,c3)]

np.logical चल रहा है और तेज़ है, लेकिन दो से अधिक तर्क नहीं लेता है, जो कि configools.reduce द्वारा नियंत्रित किया जाता है।

ध्यान दें कि इसमें अभी भी कुछ अतिरेक हैं: a) वैश्विक स्तर पर शॉर्टकटिंग नहीं होती है) प्रत्येक व्यक्तिगत स्थिति पूरे प्रारंभिक डेटा पर चलती है। फिर भी, मैं यह उम्मीद करता हूं कि यह कई अनुप्रयोगों के लिए पर्याप्त कुशल है और यह बहुत पठनीय है।

आप np.logical_orइसके बजाय उपयोग करके एक विघटन भी कर सकते हैं (जिसमें केवल एक ही स्थिति के सही होने की आवश्यकता है) :

import numpy as np
import functools
def disjunction(*conditions):
    return functools.reduce(np.logical_or, conditions)

c_1 = data.col1 == True
c_2 = data.col2 < 64
c_3 = data.col3 != 4

data_filtered = data[disjunction(c1,c2,c3)]

1
क्या यह एक चर संख्या की स्थिति के लिए लागू करने का एक तरीका है? मैं प्रत्येक जोड़कर की कोशिश की है c_1, c_2, c_3, ... c_nकिसी सूची में, और उसके बाद से गुजर रहा data[conjunction(conditions_list)]है, लेकिन कोई त्रुटि मिलती है ValueError: Item wrong length 5 instead of 37.इसके अलावा करने की कोशिश की data[conjunction(*conditions_list)], लेकिन मैं तुलना में एक अलग परिणाम प्राप्त data[conjunction(c_1, c_2, c_3, ... c_n )], नहीं यकीन है कि क्या हो रहा है।
user5359531 3

त्रुटि का समाधान कहीं और मिला। data[conjunction(*conditions_list)]डेटाफ़्रेम को एक सूची में पैक करने के बाद काम करता है, और सूची को जगह में
उतार

1
मैंने बहुत ही ढलान वाले संस्करण के साथ उपरोक्त उत्तर पर एक टिप्पणी छोड़ दी, और फिर आपके उत्तर पर ध्यान दिया। बहुत साफ है, मुझे यह बहुत पसंद है!
dwanderson

यह एक महान जवाब है!
चार्ली क्राउन

1
मैं इस्तेमाल किया था: आदि के df[f_2 & f_3 & f_4 & f_5 ]साथ f_2 = df["a"] >= 0उस समारोह के लिए कोई ज़रूरत नहीं है ... (उच्च आदेश समारोह का अच्छा उपयोग हालांकि ...)
ए। Rabus

19

सभी समाधानों में सबसे सरल:

उपयोग:

filtered_df = df[(df['col1'] >= 1) & (df['col1'] <= 5)]

एक और उदाहरण , फरवरी 2018 से संबंधित मानों के लिए डेटाफ्रेम को फ़िल्टर करने के लिए, नीचे दिए गए कोड का उपयोग करें

filtered_df = df[(df['year'] == 2018) & (df['month'] == 2)]

मैं स्थिर के बजाय चर का उपयोग कर रहा हूँ। त्रुटि हो रही है। df [df []] [df []] चेतावनी संदेश देता है लेकिन सही उत्तर देता है।
नगुई अल

8

पांडा 0.22 अद्यतन के बाद से , तुलना विकल्प उपलब्ध हैं:

  • जीईटी (इससे बड़ा)
  • लेफ्टिनेंट (से कम)
  • eq (के बराबर)
  • ne (बराबर नहीं)
  • जीई (से अधिक या बराबर)

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

# sample data
df = pd.DataFrame({'col1': [0, 1, 2,3,4,5], 'col2': [10, 11, 12,13,14,15]})

# get values from col1 greater than or equals to 1
df.loc[df['col1'].ge(1),'col1']

1    1
2    2
3    3
4    4
5    5

# where co11 values is better 0 and 2
df.loc[df['col1'].between(0,2)]

 col1 col2
0   0   10
1   1   11
2   2   12

# where col1 > 1
df.loc[df['col1'].gt(1)]

 col1 col2
2   2   12
3   3   13
4   4   14
5   5   15

2

ऐसा क्यों नहीं करते?

def filt_spec(df, col, val, op):
    import operator
    ops = {'eq': operator.eq, 'neq': operator.ne, 'gt': operator.gt, 'ge': operator.ge, 'lt': operator.lt, 'le': operator.le}
    return df[ops[op](df[col], val)]
pandas.DataFrame.filt_spec = filt_spec

डेमो:

df = pd.DataFrame({'a': [1,2,3,4,5], 'b':[5,4,3,2,1]})
df.filt_spec('a', 2, 'ge')

परिणाम:

   a  b
 1  2  4
 2  3  3
 3  4  2
 4  5  1

आप देख सकते हैं कि कॉलम 'a' फ़िल्टर किया गया है जहाँ a = = 2 है।

ऑपरेटर की तुलना में यह थोड़ा तेज है (टाइपिंग टाइम, प्रदर्शन नहीं)। आप निश्चित रूप से फ़ाइल के शीर्ष पर आयात डाल सकते हैं।


1

e किसी स्तंभ के मानों के आधार पर पंक्तियों का चयन कर सकता है जो किसी सूची या किसी पुनरावृत्ति में नहीं हैं। हम पहले की तरह ही बूलियन वैरिएबल बनाएंगे, लेकिन अब हम ~ के सामने रखकर बूलियन वैरिएबल को नकार देंगे।

उदाहरण के लिए

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