अध्याय 05

पंक्तियों को फ़िल्टर करना — हर पंक्ति से एक ही सवाल पूछना

केवल वही पंक्तियाँ रखना जो शर्त पूरी करती हैं: बूलियन मास्क, &, | और ~ से शर्तें जोड़ना, isin, between और .str मेथड्स, .loc से फ़िल्टर करना — और बिना किसी साइलेंट एरर के सीधे पंक्तियों का मान बदलना।

45 मिनटPython 3.12
  1. 1समस्या
  2. 2समझें
  3. 3उदाहरण
  4. 4अनुमान
  5. 5स्वयं करें
  6. 6चुनौती

वह समस्या जिसे हम हल कर रहे हैं

दुकान के मैनेजर का एक संदेश आता है: "उत्तरी शाखा (north branch) के कौन से ऑर्डर तीन से अधिक वस्तुओं के थे? मैं उन्हें देखना चाहता हूँ।"

पिछले अध्याय के आठ ऑर्डरों के साथ आप केवल देखकर ही उत्तर दे सकते थे। अपनी उंगली branch कॉलम पर नीचे ले जाएँ, हर north पर रुकें, और पास के quantity कॉलम को देखें। तीन पंक्तियाँ। काम पूरा।

अब वास्तविक फ़ाइल की कल्पना करें — बारह हज़ार ऑर्डर, पूरे एक साल का डेटा। अब केवल देखकर काम नहीं चलेगा, और अधिकांश पायथन प्रोग्रामरों के दिमाग में जो पहला विचार आता है, वह एक लूप (loop) है:

python
import pandas as pd

orders = pd.read_csv("orders.csv")

keep = []
for i in range(len(orders)):
    row = orders.iloc[i]
    if row["branch"] == "north" and row["quantity"] > 3:
        keep.append(int(row["order_id"]))

print(keep)
text
[1001, 1005, 1008]

उत्तर सही है। लेकिन देखिए इसके लिए क्या कीमत चुकानी पड़ी: एक काउंटर, एक खाली सूची (list), एक if, एक append, और पूरी तालिका को एक-एक पंक्ति करके तोड़ना। और परिणाम अब कोई टेबल भी नहीं रहा — यह केवल ऑर्डर नंबरों की एक साधारण सूची है। मैनेजर को उत्पाद और कीमतें भी दिखाने के लिए, आपको फिर से लूप चलाना होगा। बारह हज़ार पंक्तियों पर यह बहुत धीमा भी होगा, क्योंकि प्रत्येक orders.iloc[i] केवल दो मान पढ़ने के लिए हर बार एक नई Series बनाता है।

पांडास में यह सवाल पूछने का एक बिल्कुल अलग तरीका है, और DataFrame के साथ आप सबसे अधिक यही काम करेंगे: एक साथ हर पंक्ति से वही 'हाँ' या 'ना' वाला सवाल पूछें, और केवल उन पंक्तियों को रखें जिन्होंने 'हाँ' कहा। यही फ़िल्टरिंग (filtering) है, और यह पूरा अध्याय इसी के बारे में है। ऊपर दिया गया लूप सिर्फ़ एक लाइन में बदल जाता है, परिणाम एक टेबल के रूप में ही रहता है, और यह पढ़ने में लगभग मैनेजर के वाक्य जैसा ही लगता है।

इस अध्याय के अंत में आप कर पाएंगे

  • किसी प्रश्न को बूलियन मास्क (boolean mask) में बदलना — यानी प्रति पंक्ति True/False का एक कॉलम — और पंक्तियों को रखने या mask.sum() व mask.mean() से उन्हें गिनने के लिए इसका उपयोग करना
  • &, | और ~ के साथ शर्तों को जोड़ना, और समझाना कि प्रत्येक शर्त के चारों ओर कोष्ठक लगाना क्यों अनिवार्य है
  • सूचियों, श्रेणियों और टेक्स्ट पैटर्न के लिए isin, between और .str विधियों का उपयोग करना
  • .loc[mask, columns] के साथ एक ही चरण में पंक्तियों को फ़िल्टर करना और कॉलम चुनना
  • .loc[mask, column] = value के साथ फ़िल्टर की गई पंक्तियों में सुरक्षित रूप से मान बदलना, और पांडास 3 में असुरक्षित तरीके से आने वाली ChainedAssignmentError को पहचानना

ज़रूरी शर्तें: कॉलम और पंक्तियाँ चुनना।


पहले फ़ाइल बनाएँ

इस अध्याय में पिछले अध्याय की तरह ही orders.csv का उपयोग किया गया है। यदि आपके पास यह नहीं है, तो अपने प्रोजेक्ट फ़ोल्डर में ठीक इन नौ पंक्तियों के साथ इसे बनाएँ:

text
order_id,date,branch,product,category,quantity,price
1001,2024-03-01,north,pen,stationery,12,15.0
1002,2024-03-01,south,notebook,stationery,5,60.0
1003,2024-03-02,north,bag,accessories,2,850.0
1004,2024-03-02,east,bottle,accessories,7,120.0
1005,2024-03-03,north,eraser,stationery,30,8.0
1006,2024-03-03,south,bag,accessories,1,850.0
1007,2024-03-04,east,pen,stationery,20,15.0
1008,2024-03-04,north,bottle,accessories,4,120.0

कोड लिखने से पहले

फ़िल्टर कोड में लिखा गया एक प्रश्न है। फ़िल्टरिंग के अधिकांश बग पांडास के बग नहीं होते — वे ऐसे प्रश्न होते हैं जिन्हें ठीक से समझा नहीं गया था, या कोई ऐसा कॉलम होता है जिसमें वह मान नहीं होता जो आपने सोचा था। इसलिए फ़िल्टरिंग की पहली लाइन लिखने से पहले, ये पाँच काम करें।

1. प्रश्न को शर्तों के रूप में लिखें, प्रति कॉलम एक शर्त। मैनेजर के वाक्य को अलग-अलग भागों में तोड़ें:

  • "उत्तरी शाखा (north branch) के ऑर्डर" → कॉलम branch, तुलना बराबर (equals), मान "north"
  • "तीन से अधिक वस्तुएं" → कॉलम quantity, तुलना बड़ा (greater than), मान 3
  • "मैं उन्हें देखना चाहता हूँ" → कोई तुलना नहीं; पूरी पंक्ति रखें

यह सूची आपको दो निर्णय लेने पर मजबूर करती है। "तीन से अधिक" का अर्थ है > 3, >= 3 नहीं — यानी ठीक तीन वस्तुओं वाला ऑर्डर शामिल नहीं है। और दोनों शर्तें and (तथा) द्वारा जुड़ी हैं: किसी ऑर्डर को दोनों शर्तों को पास करना होगा। ये दोनों निर्णय ही पूरे फ़िल्टर का आधार हैं; कोड केवल उनकी वर्तनी है।

2. इनपुट की जाँच करें — आकार (shape), प्रकार (dtypes), और वे सटीक मान जिनसे आप तुलना करेंगे।

python
import pandas as pd

orders = pd.read_csv("orders.csv")

print(orders.shape)
print(orders.dtypes)
text
(8, 7)
order_id      int64
date            str
branch          str
product         str
category        str
quantity      int64
price       float64
dtype: object

quantity कॉलम का प्रकार int64 है, इसलिए > 3 संख्याओं की तुलना करेगा। यदि यह str के रूप में आया होता — क्योंकि किसी ने एक सेल में "3 pcs" टाइप कर दिया था — तो तुलना TypeError के साथ विफल हो जाती; और आप यह बात अभी जानना चाहते हैं, काम के बीच में नहीं। branch कॉलम str है, इसलिए इसकी तुलना टेक्स्ट के रूप में की जाएगी, और टेक्स्ट की तुलना बिल्कुल सटीक होती है: "north", "North" और "north " तीन अलग-अलग मान हैं। इसलिए कॉलम में वास्तव में मौजूद मानों को देखें:

python
print(orders["branch"].unique())
text
<StringArray>
['north', 'south', 'east']
Length: 3, dtype: str

तीन शाखाएँ, प्रत्येक की एक ही वर्तनी, कोई अतिरिक्त स्पेस नहीं। अब कोड में "north" का मेल किसी न किसी पंक्ति से होना तय है।

3. आउटपुट जनरेट करने से पहले उसका एक अनुमानित खाका बनाएँ। एक छोटी फ़ाइल पर, पहले हाथ से सवाल का जवाब दें। उत्तरी शाखा के ऑर्डर हैं 1001 (12 वस्तुएं), 1003 (2), 1005 (30) और 1008 (4)। तीन से अधिक वस्तुएं: 1001, 1005, 1008। इसलिए अपेक्षित परिणाम 3 पंक्तियाँ, सभी 7 कॉलम हैं, और उनके ऑर्डर नंबर पहले से ज्ञात हैं। यदि कोड बाद में 4 पंक्तियाँ या 0 पंक्तियाँ लौटाता है, तो आप उस पर आँख मूंदकर भरोसा करने के बजाय तुरंत समझ जाएंगे कि कोड गलत है। एक बड़ी फ़ाइल पर आप यह सब हाथ से नहीं कर सकते, लेकिन फिर भी आप एक मोटा अनुमान लगा सकते हैं: "उत्तरी शाखा में लगभग आधे ऑर्डर आते हैं, इसलिए उत्तर कुल पंक्तियों के आधे से काफी कम होना चाहिए।"

4. शर्त के स्वरूप के आधार पर सही टूल चुनें।

  • एक तुलना → एक मास्क: df["col"] > value
  • कई शर्तें → & (and), | (or), ~ (not) से जुड़े मास्क
  • "इन मानों में से कोई एक" → df["col"].isin([...])
  • "दो मानों के बीच" → df["col"].between(low, high)
  • टेक्स्ट के अंदर एक पैटर्न → df["col"].str.contains(...), .str.startswith(...)
  • पंक्तियाँ और केवल कुछ कॉलम → df.loc[mask, ["col1", "col2"]]
  • उन पंक्तियों में मान बदलना → df.loc[mask, "col"] = new_value

मैनेजर का प्रश्न and द्वारा जुड़ी दो तुलनाएँ हैं, इसलिए: दो मास्क और &।

5. तय करें कि आप उत्तर की जाँच कैसे करेंगे। फ़िल्टर करने के बाद, तीन आसान जाँचें लगभग हर गलती को पकड़ लेती हैं: पंक्तियों की संख्या आपके अनुमान से मेल खाती है; बची हुई हर पंक्ति में शर्त वास्तव में लागू होती है ((result["quantity"] > 3).all() से True आना चाहिए); और आपकी उम्मीद की कोई भी चीज़ गायब नहीं है (परिणाम में 1008 को देखें)।

अब कोड पर आते हैं — एक समय में एक विचार।


तुलना करने से एक मास्क मिलता है

किसी कॉलम की तुलना किसी मान से करें, और पांडास आपको केवल एक True या False नहीं देता। यह प्रत्येक पंक्ति की तुलना करता है और आपको उत्तरों का एक पूरा कॉलम देता है:

python
big = orders["quantity"] > 5

print(big)
text
0     True
1    False
2    False
3     True
4     True
5    False
6     True
7    False
Name: quantity, dtype: bool

यह bool की एक Series है — एक बूलियन मास्क (boolean mask)। इसका इंडेक्स orders के समान ही है (0 से 7), प्रति पंक्ति एक उत्तर: पंक्ति 0 में 12 वस्तुएं हैं, इसलिए True; पंक्ति 1 में 5 हैं, और 5, 5 से बड़ा नहीं है, इसलिए False।

पांडास लूप चलाने के बजाय इस तरह काम क्यों करता है? क्योंकि एक कॉलम मेमोरी में एक ही प्रकार की संख्याओं के एक ब्लॉक के रूप में संग्रहीत होता है, और एक पूरे ब्लॉक की तुलना 5 से करना तेज़ संकलित (compiled) कोड में किया जाने वाला एक सिंगल ऑपरेशन है। आपके लूप ने पायथन से प्रत्येक पंक्ति को अलग से आठ बार — या बारह हज़ार बार — देखने के लिए कहा था। मास्क एक ही बार में यह काम करता है।

प्रत्येक तुलना ऑपरेटर इसी तरह काम करता है: ==, !=, <, <=, >, >=। वे टेक्स्ट कॉलम पर भी काम करते हैं — orders["branch"] == "north" एक मास्क है जिसमें पंक्तियों 0, 2, 4 और 7 में True होता है।

अभी तक कुछ भी फ़िल्टर नहीं हुआ है। मास्क केवल उत्तरों की सूची है। अगला कदम उनका उपयोग करना है।

मास्क पंक्तियों को रखता है

मास्क को स्क्वायर ब्रैकेट्स (चौकोर कोष्ठक) के अंदर रखें, और पांडास उन पंक्तियों को रख लेता है जहाँ मास्क True है:

python
print(orders[big])
text
order_id        date branch product     category  quantity  price
0      1001  2024-03-01  north     pen   stationery        12   15.0
3      1004  2024-03-02   east  bottle  accessories         7  120.0
4      1005  2024-03-03  north  eraser   stationery        30    8.0
6      1007  2024-03-04   east     pen   stationery        20   15.0

आठ में से चार पंक्तियाँ। बाईं ओर नीचे इंडेक्स पढ़ें: 0, 3, 4, 6। बची हुई पंक्तियाँ अपने मूल लेबल बनाए रखती हैं। फ़िल्टरिंग किसी भी चीज़ को दोबारा नंबर नहीं देती — पंक्ति 3 अभी भी पंक्ति 3 ही है, वही ऑर्डर 1004 जो पूरी टेबल में था। यह बात बहुत महत्वपूर्ण साबित होती है, और हम इस पर वापस आएंगे।

पांडास मास्क को पंक्तियों के साथ कैसे मिलाता है? स्थिति (position) के आधार पर नहीं — बल्कि इंडेक्स लेबल (index label) के आधार पर। मास्क का लेबल 0 पंक्ति 0 का फैसला करता है, लेबल 3 पंक्ति 3 का फैसला करता है। यहाँ दोनों इंडेक्स एक जैसे हैं, क्योंकि मास्क स्वयं orders से ही बनाया गया था, इसलिए यह अंतर दिखाई नहीं देता। यह केवल तब दिखाई देता है जब आप किसी भिन्न टेबल से बनाए गए मास्क का उपयोग करते हैं — जिसे "समस्याएँ और समाधान" में समझाया गया है।

आप आमतौर पर मास्क को बिना किसी अलग नाम के सीधे लिखा हुआ देखेंगे:

python
print(orders[orders["branch"] == "north"])
text
order_id        date branch product     category  quantity  price
0      1001  2024-03-01  north     pen   stationery        12   15.0
2      1003  2024-03-02  north     bag  accessories         2  850.0
4      1005  2024-03-03  north  eraser   stationery        30    8.0
7      1008  2024-03-04  north  bottle  accessories         4  120.0

इसे अंदर से बाहर की ओर पढ़ें: `orders["branch"] == "north"` मास्क बनाता है, और `orders[ … ]` True पंक्तियों को रखता है। जब शर्त लंबी हो या जब आप इसे दो बार उपयोग करते हैं तो मास्क को एक नाम देना (big = …) उपयोगी होता है; छोटी शर्तों के लिए इसे सीधे इनलाइन लिखना ठीक है।

बिना फ़िल्टर किए मास्क से गिनती करना

अक्सर मैनेजर को पूरी पंक्तियाँ नहीं चाहिए होती हैं, केवल एक संख्या चाहिए होती है: पाँच से अधिक वस्तुओं वाले कितने ऑर्डर थे? इसके लिए आपको फ़िल्टर करने की आवश्यकता नहीं है। अंकगणित में, True को 1 और False को 0 गिना जाता है, इसलिए:

python
print(big.sum())
print(big.mean())
text
4
0.5

sum() सभी एक (ones) को जोड़ता है: 4 ऑर्डर। mean() पंक्तियों की कुल संख्या से भाग देता है: 8 में से 4 यानी 0.5, जिसका अर्थ है कि आधे ऑर्डर पाँच से अधिक वस्तुओं के थे। "कितने" के लिए मास्क का .sum() निकालें; "कितना हिस्सा या प्रतिशत" के लिए मास्क का .mean() निकालें। दोनों एक ही पंक्ति के काम हैं और दोनों में से कोई भी नई टेबल नहीं बनाता।

और जब आप फ़िल्टर की गई पंक्तियों के किसी कॉलम का कुल योग चाहते हैं — उन बड़े ऑर्डरों में वस्तुओं की कुल संख्या — तो पहले फ़िल्टर करें, फिर कॉलम चुनें, फिर योग करें:

python
print(orders[big]["quantity"].sum())
text
69

12 + 7 + 30 + 20 = 69। फ़िल्टर की गई टेबल के किसी कॉलम को इस तरह पढ़ना (read) पूरी तरह सुरक्षित है। लेकिन इस तरह से उसमें मान लिखना (write) गलत है — वह इस अध्याय का अंतिम भाग है।


कई शर्तें: &, |, ~

मैनेजर के प्रश्न में दो शर्तें हैं। पांडास तीन ऑपरेटरों के साथ मास्क को जोड़ता है:

  • & का अर्थ है and — पंक्ति तब रखी जाती है जब दोनों मास्क True हों
  • | का अर्थ है or — पंक्ति तब रखी जाती है जब कम से कम एक मास्क True हो
  • ~ का अर्थ है not — यह प्रत्येक True को False में और False को True में बदल देता है

यहाँ अंततः मैनेजर का प्रश्न हल किया गया है:

python
north_big = orders[(orders["branch"] == "north") & (orders["quantity"] > 3)]

print(north_big)
text
order_id        date branch product     category  quantity  price
0      1001  2024-03-01  north     pen   stationery        12   15.0
4      1005  2024-03-03  north  eraser   stationery        30    8.0
7      1008  2024-03-04  north  bottle  accessories         4  120.0

तीन पंक्तियाँ: 1001, 1005, 1008 — बिल्कुल योजना के चरण से मिला खाका। अब चरण 5 से जाँच:

python
print(len(north_big))
print((north_big["quantity"] > 3).all())
print((north_big["branch"] == "north").all())
text
3
True
True

पंक्तियों की संख्या मेल खाती है, और बची हुई प्रत्येक पंक्ति में दोनों शर्तें लागू होती हैं। .all() पूछता है "क्या इस मास्क का प्रत्येक मान True है?" — यह साबित करने का सबसे तेज़ तरीका है कि फ़िल्टर ने वही किया जो आप चाहते थे।

एक or (या) प्रश्न: पूर्वी शाखा के ऑर्डर, या 850 या उससे अधिक कीमत वाले किसी भी उत्पाद का कोई भी ऑर्डर।

python
print(orders[(orders["branch"] == "east") | (orders["price"] >= 850)])
text
order_id        date branch product     category  quantity  price
2      1003  2024-03-02  north     bag  accessories         2  850.0
3      1004  2024-03-02   east  bottle  accessories         7  120.0
5      1006  2024-03-03  south     bag  accessories         1  850.0
6      1007  2024-03-04   east     pen   stationery        20   15.0

चार पंक्तियाँ: दो पूर्वी शाखा के ऑर्डर, और उत्तर तथा दक्षिण से दो बैग के ऑर्डर। जो पंक्ति दोनों शर्तों को पूरा करती है (यहाँ ऐसी कोई नहीं है), वह केवल एक बार दिखाई देगी, दो बार नहीं — क्योंकि फ़िल्टर केवल किसी पंक्ति को रखता है या छोड़ता है।

एक not (नहीं) प्रश्न: स्टेशनरी (stationery) को छोड़कर बाकी सब कुछ।

python
print(orders[~(orders["category"] == "stationery")])
text
order_id        date branch product     category  quantity  price
2      1003  2024-03-02  north     bag  accessories         2  850.0
3      1004  2024-03-02   east  bottle  accessories         7  120.0
5      1006  2024-03-03  south     bag  accessories         1  850.0
7      1008  2024-03-04  north  bottle  accessories         4  120.0

~ प्रत्येक True को False में और इसके विपरीत बदल देता है। एक एकल समानता के लिए आप इसके बजाय != लिख सकते थे, जो कि सरल है; ~ अपनी जगह तब बनाता है जब आप किसी लंबी शर्त या isin को नकार रहे हों (जिसे आप जल्द ही देखेंगे)।

कोष्ठक लगाना क्यों अनिवार्य है

ऊपर दी गई प्रत्येक शर्त अपने स्वयं के कोष्ठक के अंदर है। यह कोई कोडिंग स्टाइल नहीं है। उन्हें हटा दें और कोड टूट जाएगा:

python
print(orders[orders["quantity"] > 3 & orders["price"] < 100])
text
TypeError: Cannot perform 'rand_' with a dtyped [float64] array and scalar of type [bool]

ऊपर जो आप देख रहे हैं वह ट्रेसबैक की अंतिम पंक्ति है, वह पंक्ति जो त्रुटि का नाम बताती है; इसके ऊपर की पंक्तियाँ केवल यह बताती हैं कि यह कहाँ हुआ। इसका कारण ऑपरेटर प्राथमिकता (operator precedence) है — यानी वह क्रम जिसमें पायथन ऑपरेटरों को लागू करता है। पायथन में, & की प्राथमिकता > और < से अधिक होती है। यह पांडास के अस्तित्व में आने से बहुत पहले तय किया गया था, क्योंकि & पूर्ण संख्याओं (बिटवाइज़ "and") के लिए डिज़ाइन किया गया था, जहाँ इसे पहले होना चाहिए। इसलिए पायथन इस पंक्ति को इस प्रकार पढ़ता है:

text
orders["quantity"] > (3 & orders["price"]) < 100

यह पहले 3 & orders["price"] की गणना करने का प्रयास करता है — यानी संख्या 3 और दशमलव संख्याओं के कॉलम के बीच एक बिटवाइज़ "and" — और यही कारण है कि यह त्रुटि आती है। संदेश में rand_ (दाहिने हाथ का "and") और एक ऐरे का उल्लेख होता है, लेकिन कारण हमेशा एक ही होता है: कोष्ठक का छूटना।

त्रुटि संदेश आना वास्तव में अच्छी बात है। यदि आप केवल पहली शर्त को कोष्ठक में लपेटते हैं और दूसरी को भूल जाते हैं, तो कोई एरर नहीं आता — और वही असली खतरा है:

python
print(len(orders[(orders["quantity"] > 3) & orders["price"] < 100]))
print(len(orders[(orders["quantity"] > 3) & (orders["price"] < 100)]))
text
8
4

पहली पंक्ति को ((quantity > 3) & price) < 100 के रूप में पढ़ा जाता है। & मास्क को कीमतों के साथ मिला देता है, और संयोग से हर परिणाम 100 से कम होता है, इसलिए यह "फ़िल्टर" सभी आठ पंक्तियों को रख लेता है। जबकि सही फ़िल्टर चार पंक्तियों को रखता है। वही कॉलम, वही संख्याएँ, बस कोष्ठक का एक जोड़ा गायब — और गलत संस्करण बिना किसी आवाज़ के चल जाता है। हर बार, हर शर्त को उसके अपने कोष्ठक में रखें। तब न तो कोई त्रुटि हो सकती है और न ही कोई शांत विफलता।

and का उपयोग क्यों नहीं?

स्वाभाविक प्रवृत्ति अंग्रेज़ी शब्द लिखने की होती है:

python
print(orders[(orders["branch"] == "north") and (orders["quantity"] > 3)])
text
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

पायथन के and, or और not को प्रत्येक तरफ एक एकल True या False की आवश्यकता होती है — वे if कथनों के लिए बनाए गए थे। and का उपयोग करने के लिए, पायथन पहले मास्क से पूछता है "क्या आप सच हैं?", और आठ उत्तरों का एक कॉलम एक उत्तर नहीं दे सकता। क्या यह तब सच है जब कोई भी मान True हो? या जब सभी सच हों? पांडास अनुमान लगाने से इनकार करता है, और एरर उन तरीकों को सूचीबद्ध करता है जिनसे आप निर्णय ले सकते हैं (a.any(), a.all())। लेकिन आप एक उत्तर नहीं चाहते थे; आप पंक्ति-दर-पंक्ति "and" चाहते थे। वह & है। याद रखने का नियम: फ़िल्टर के अंदर, and/or/not बदलकर &/|/~ बन जाते हैं।


कई मानों में से एक: isin

दक्षिणी या पूर्वी शाखा के ऑर्डर। आप | से जुड़ी दो समानताएँ लिख सकते हैं। पाँच शाखाओं के साथ यह पाँच तुलनाएँ बन जाती हैं। isin एक सूची लेता है और पूछता है "क्या इस पंक्ति का मान सूची में है?":

python
print(orders[orders["branch"].isin(["east", "south"])])
text
order_id        date branch   product     category  quantity  price
1      1002  2024-03-01  south  notebook   stationery         5   60.0
3      1004  2024-03-02   east    bottle  accessories         7  120.0
5      1006  2024-03-03  south       bag  accessories         1  850.0
6      1007  2024-03-04   east       pen   stationery        20   15.0

और यहीं ~ अपनी उपयोगिता साबित करता है — उत्तर और पूर्व को छोड़कर हर शाखा का अर्थ है isin के आगे ~ लगाना:

python
print(orders[~orders["branch"].isin(["north", "east"])]["order_id"].tolist())
text
[1002, 1006]

दो ऑर्डर, दोनों दक्षिणी शाखा से। ध्यान दें कि ~orders["branch"].isin([...]) को किसी अतिरिक्त कोष्ठक की आवश्यकता नहीं है: ~ विधि कॉल के परिणाम पर लागू होता है, और यहाँ संघर्ष करने के लिए कोई & या | नहीं है। जैसे ही आप इसे किसी अन्य शर्त के साथ जोड़ते हैं, कोष्ठक वापस आ जाते हैं: (~orders["branch"].isin([...])) & (orders["quantity"] > 3)।

एक सीमा (range): between

15 से 120 मूल्य वाले ऑर्डर। यह दो तुलनाएँ हैं, >= 15 और <= 120। between इसे एक में कह देता है:

python
print(orders[orders["price"].between(15, 120)])
text
order_id        date branch   product     category  quantity  price
0      1001  2024-03-01  north       pen   stationery        12   15.0
1      1002  2024-03-01  south  notebook   stationery         5   60.0
3      1004  2024-03-02   east    bottle  accessories         7  120.0
6      1007  2024-03-04   east       pen   stationery        20   15.0
7      1008  2024-03-04  north    bottle  accessories         4  120.0

between डिफ़ॉल्ट रूप से दोनों सिरों को शामिल करता है — ठीक 15.0 पर पेन और ठीक 120.0 पर बोतलें दोनों इसमें शामिल हैं। यह इस बात से मेल खाता है कि लोग आमतौर पर "15 से 120" कैसे कहते हैं, लेकिन प्रश्न के संदर्भ में इसकी पुष्टि कर लें। यदि प्रश्न का अर्थ स्पष्ट रूप से बीच का है, तो वैसा निर्दिष्ट करें:

python
print(orders["price"].between(15, 120, inclusive="neither").sum())
text
1

केवल 60.0 वाली नोटबुक ही विशेष रूप से बीच में है। inclusive आधे-खुले रेंज के लिए "left" और "right" भी स्वीकार करता है — जो पैसे के स्लैब के लिए सामान्य विकल्प है, जहाँ आप सीमा मान को दो श्रेणियों में गिने बिना 0–100, 100–200 आदि चाहते हैं।

टेक्स्ट पैटर्न: .str विधियाँ

टेक्स्ट पर सामान्य समानता सटीक होती है। .str एक्सेसॉर टेक्स्ट कॉलम को पायथन की जानी-पहचानी स्ट्रिंग विधियाँ प्रदान करता है, जो प्रत्येक पंक्ति पर लागू होती हैं, और प्रत्येक एक मास्क लौटाती है।

वे उत्पाद जिनका नाम "b" से शुरू होता है:

python
print(orders[orders["product"].str.startswith("b")])
text
order_id        date branch product     category  quantity  price
2      1003  2024-03-02  north     bag  accessories         2  850.0
3      1004  2024-03-02   east  bottle  accessories         7  120.0
5      1006  2024-03-03  south     bag  accessories         1  850.0
7      1008  2024-03-04  north  bottle  accessories         4  120.0

वे उत्पाद जिनमें "o" अक्षर शामिल है, चाहे वह किसी भी केस में हो:

python
print(orders[orders["product"].str.contains("O", case=False)])
text
order_id        date branch   product     category  quantity  price
1      1002  2024-03-01  south  notebook   stationery         5   60.0
3      1004  2024-03-02   east    bottle  accessories         7  120.0
7      1008  2024-03-04  north    bottle  accessories         4  120.0

डेटा में कहीं भी कैपिटल O नहीं है, फिर भी नोटबुक और दो बोतलें मिल जाती हैं, क्योंकि case=False केस को अनदेखा करता है। इसके बिना, .str.contains("O") को कुछ नहीं मिलता।

सटीक समानता में ऐसा कोई विकल्प नहीं होता है। यह सबसे आम "मेरा फ़िल्टर कुछ भी नहीं लौटाता" वाला बग है:

python
print(orders[orders["branch"] == "North"])
text
Empty DataFrame
Columns: [order_id, date, branch, product, category, quantity, price]
Index: []

कोई एरर नहीं, कोई पंक्ति नहीं। टेबल गलत नहीं है और पांडास भी गलत नहीं है — बस कैपिटल लेटर में लिखा "North" नाम का कोई ब्रांच ही नहीं है। यही कारण है कि योजना के चरण 2 में unique() प्रिंट किया गया था: जिन मानों से आप तुलना करते हैं उन्हें कॉलम में वास्तव में जो है उससे कॉपी किया जाना चाहिए, न कि याददाश्त से टाइप किया जाना चाहिए।

टेक्स्ट तारीखें सही ढंग से तुलना करती हैं — जब वे ISO प्रारूप में हों

date कॉलम str के रूप में आया। क्या आप इसे >= से फ़िल्टर कर सकते हैं? 3 मार्च से आगे के ऑर्डर:

python
print(orders["date"] >= "2024-03-03")
text
0    False
1    False
2    False
3    False
4     True
5     True
6     True
7     True
Name: date, dtype: bool

यह काम करता है, लेकिन समझें कि क्यों। ये स्ट्रिंग्स हैं, इसलिए >= उनकी टेक्स्ट के रूप में तुलना करता है, अक्षर दर अक्षर, जिस तरह डिक्शनरी में शब्दों को क्रमबद्ध किया जाता है। यह सही उत्तर केवल इसलिए देता है क्योंकि तारीखें वर्ष-महीना-दिन प्रारूप में शून्य के साथ लिखी जाती हैं (2024-03-03), इसलिए टेक्स्ट का क्रम समय के क्रम के बराबर हो जाता है। 3/3/2024 लिखी गई तारीख की गलत तुलना होगी — क्योंकि "1", "9" से पहले आता है, इसलिए "10/1/2024" टेक्स्ट "9/1/2024" से पहले आ जाएगा। साधारण कट-ऑफ से परे किसी भी चीज़ के लिए, pd.to_datetime के साथ वास्तविक तारीखों में बदलें, जिसे कॉलम जोड़ने के अध्याय में शामिल किया गया है।


पंक्तियाँ और कॉलम एक साथ: .loc[mask, columns]

अब तक के फ़िल्टरों ने हर कॉलम को सुरक्षित रखा। मैनेजर उत्तरी शाखा के ऑर्डर चाहता था, लेकिन रिपोर्ट को केवल उत्पाद और मात्रा की आवश्यकता है। पिछले अध्याय में आपने लेबल के साथ .loc[rows, columns] देखा था। पंक्तियों वाले हिस्से में मास्क भी स्वीकार किया जाता है:

python
print(orders.loc[orders["branch"] == "north", ["product", "quantity"]])
text
product  quantity
0     pen        12
2     bag         2
4  eraser        30
7  bottle         4

एक चरण, एक ऑब्जेक्ट, और आपके कोड का पाठक पूरे प्रश्न को — कौन सी पंक्तियाँ, कौन से कॉलम — एक ही स्थान पर देखता है। .loc[mask, "quantity"] (एकल कॉलम का नाम, सूची नहीं) आपको इसके बजाय एक Series देता है, जो अंकगणित के लिए तैयार है:

python
print(orders.loc[orders["branch"] == "north", "quantity"].sum())
text
48

12 + 2 + 30 + 4 = उत्तरी शाखा द्वारा बेची गई 48 वस्तुएं।

query(): वाक्य के रूप में वही फ़िल्टर

फ़िल्टर के लिए एक दूसरा तरीका query() है, जो शर्त को एक स्ट्रिंग के रूप में लेता है:

python
print(orders.query("branch == 'north' and quantity > 3"))
text
order_id        date branch product     category  quantity  price
0      1001  2024-03-01  north     pen   stationery        12   15.0
4      1005  2024-03-03  north  eraser   stationery        30    8.0
7      1008  2024-03-04  north  bottle  accessories         4  120.0

वही तीन पंक्तियाँ। स्ट्रिंग के अंदर आप सीधे कॉलम नाम लिखते हैं, और and/or/not की अनुमति है, क्योंकि query स्ट्रिंग को पायथन के ऑपरेटरों को सौंपने के बजाय स्वयं पार्स करता है। किसी पायथन वेरिएबल का उपयोग करने के लिए, उसके पहले @ लगाएं:

python
min_qty = 5
print(orders.query("quantity >= @min_qty")["order_id"].tolist())
text
[1001, 1002, 1004, 1005, 1007]

लंबी शर्तों के लिए query पढ़ने में बहुत अच्छा लगता है। इसकी कमियाँ: स्ट्रिंग के अंदर टाइपो का पता केवल लाइन चलने पर ही चलता है, आपका एडिटर इसके अंदर आपकी मदद नहीं कर सकता, और स्पेस वाले कॉलम नामों के लिए बैकबिक्स की आवश्यकता होती है। यह पाठ्यक्रम मास्क को मुख्य टूल के रूप में उपयोग करता है, क्योंकि मास्क वही हैं जिन पर पांडास में बाकी सब कुछ — .loc, असाइनमेंट, गिनती — बना है। query को जानें ताकि आप इसे अन्य लोगों के कोड में पढ़ सकें।


फ़िल्टर की गई टेबल अपने पुराने लेबल बनाए रखती है

इंडेक्स पर वापस आते हैं। उत्तरी शाखा के ऑर्डर सहेजें और उन्हें देखें:

python
north = orders[orders["branch"] == "north"]

print(north.index.tolist())
text
[0, 2, 4, 7]

लेबल 0, 2, 4, 7 हैं। तो north.loc[1] क्या होगा? इस टेबल में कोई लेबल 1 नहीं है — ऑर्डर 1002 दक्षिणी शाखा से आया था और फ़िल्टर हो गया था:

python
print(north.loc[1])
text
KeyError: 1

.loc एक लेबल खोजता है, और लेबल 1 यहाँ मौजूद नहीं है। यदि आपका मतलब "दूसरा उत्तरी शाखा का ऑर्डर" था, तो वह एक स्थिति (position) है, और स्थितियाँ .iloc होती हैं:

python
print(north.iloc[1])
text
order_id           1003
date         2024-03-02
branch            north
product             bag
category    accessories
quantity              2
price             850.0
Name: 2, dtype: object

स्थिति के अनुसार दूसरी पंक्ति बैग का ऑर्डर है — और इसका Name है 2, जो मूल टेबल से इसका लेबल है। यह पिछले अध्याय से ठीक वही loc/iloc का अंतर है, और फ़िल्टरिंग वह जगह है जहाँ यह सामने आता है, क्योंकि फ़िल्टर के बाद लेबल और स्थितियाँ मेल खाना बंद कर देती हैं।

लेबल बनाए रखना जानबूझकर किया गया डिज़ाइन है: यह आपको परिणाम की किसी भी पंक्ति को मूल टेबल से जोड़ने की अनुमति देता है। जब आपको इसकी आवश्यकता न हो — जैसे कि फ़िल्टर की गई टेबल अंतिम रिपोर्ट हो — तो इसे फिर से नंबर दें:

python
print(north.reset_index(drop=True))
text
order_id        date branch product     category  quantity  price
0      1001  2024-03-01  north     pen   stationery        12   15.0
1      1003  2024-03-02  north     bag  accessories         2  850.0
2      1005  2024-03-03  north  eraser   stationery        30    8.0
3      1008  2024-03-04  north  bottle  accessories         4  120.0

drop=True पुराने लेबलों को हटा देता है। इसके बिना, उन्हें index नामक एक नए कॉलम के रूप में रखा जाएगा, जो कि रिपोर्ट में आमतौर पर आवश्यक नहीं होता है।

खाली परिणाम कोई त्रुटि नहीं है

python
none = orders[orders["branch"] == "west"]

print(none)
print(none.shape)
print(none.empty)
text
Empty DataFrame
Columns: [order_id, date, branch, product, category, quantity, price]
Index: []
(0, 7)
True

कोई पश्चिमी शाखा नहीं है, इसलिए कोई पंक्ति नहीं है — और कोई त्रुटि भी नहीं है। यह सही व्यवहार है (शून्य एक मान्य उत्तर है), लेकिन इसका मतलब है कि गलत वर्तनी वाला फ़िल्टर चुपचाप विफल हो जाता है। खाली परिणाम पर योग 0 और औसत nan होते हैं, जो किसी के ध्यान में आने से पहले रिपोर्ट में बहुत आगे तक जा सकते हैं। जब कोई फ़िल्टर किसी महत्वपूर्ण चीज़ को फ़ीड करता है, तो result.empty या पंक्ति संख्या की जाँच करें, और आउटपुट में स्पष्ट रूप से कहें: खाली टेबल प्रिंट करने से बेहतर है "No orders from the west branch." लिखना।


फ़िल्टर की गई पंक्तियों को बदलना: .loc[mask, column] = value

कभी-कभी आप कुछ बदलने के लिए फ़िल्टर करते हैं। पूर्वी शाखा द्वारा बेचे गए पेनों की कीमत गलत थी; उन्हें 15.0 नहीं, बल्कि 13.5 होना चाहिए था। लुभावना कोड पहले फ़िल्टर करता है और फिर कॉलम चुनता है — कोष्ठक के दो जोड़े, जिस तरह से आपने पहले मान पढ़े थे:

python
import pandas as pd

orders = pd.read_csv("orders.csv")

orders[orders["branch"] == "east"]["price"] = 13.5

print(orders["price"].tolist())
text
[15.0, 60.0, 850.0, 120.0, 8.0, 850.0, 15.0, 120.0]
ChainedAssignmentError: A value is being set on a copy of a DataFrame or Series through chained assignment.
Such chained assignment never works to update the original DataFrame or Series, because the intermediate object on which we are setting values always behaves as a copy (due to Copy-on-Write).

Try using '.loc[row_indexer, col_indexer] = value' instead, to perform the assignment in a single step.

See the documentation for a more detailed explanation: https://pandas.pydata.org/pandas-docs/stable/user_guide/copy_on_write.html#chained-assignment

कीमतें पढ़ें: कुछ भी नहीं बदला। और पांडास ने आपको विस्तार से चेतावनी दी। (टर्मिनल में चेतावनी stderr पर जाती है, इसलिए यह सूची के ऊपर दिखाई देती है। यहाँ इसे आउटपुट के बाद दिखाया गया है।) यह चेन्ड असाइनमेंट (chained assignment) है — दो इंडेक्सिंग चरण, orders[mask] और फिर ["price"], जिसके अंत में असाइनमेंट होता है। पहला चरण, orders[mask], पूर्वी शाखा की पंक्तियों वाली एक नई तालिका बनाता है। दूसरा चरण उस नई तालिका में कीमतें निर्धारित करता है। फिर उस नई तालिका को छोड़ दिया जाता है, क्योंकि कोई वेरिएबल उसे पकड़ कर नहीं रखता। मूल orders कभी बदला ही नहीं गया।

पांडास 3 में यह गारंटीकृत व्यवहार है, जिसे कॉपी-ऑन-राइट (Copy-on-Write) कहा जाता है: इंडेक्सिंग द्वारा प्राप्त कोई भी तालिका अपनी स्वयं की कॉपी के रूप में व्यवहार करती है, इसलिए उसमें लिखने से मूल तालिका कभी प्रभावित नहीं हो सकती। ChainedAssignmentError एक चेतावनी है, अपवाद (exception) नहीं — प्रोग्राम चलता रहता है — यही कारण है कि यह खतरनाक है: इस लाइन वाली स्क्रिप्ट पुरानी कीमतों के साथ ही "सफलतापूर्वक" समाप्त हो जाती है।

(यदि आप पुराने ट्यूटोरियल पढ़ते हैं, तो आप इस स्थिति को SettingWithCopyWarning के साथ वर्णित देखेंगे जहाँ कहा जाता था कि यह काम "कर भी सकता है और नहीं भी"। पांडास 3 में उत्तर सरल है: यह कभी काम नहीं करता, इसलिए इसे कभी न लिखें।)

इसका समाधान चेतावनी की अपनी सलाह है: एक इंडेक्सिंग चरण, .loc[rows, column], जिस पर असाइनमेंट किया जाए:

python
orders.loc[orders["branch"] == "east", "price"] = 13.5

print(orders["price"].tolist())
text
[15.0, 60.0, 850.0, 13.5, 8.0, 850.0, 13.5, 120.0]

रुकिए — इसने पूर्वी शाखा के दोनों ऑर्डरों, पेन और बोतल, को बदल दिया। बोतल की कीमत तो गलत नहीं थी। कोड ने वही किया जो लिखा गया था; कोड में पूछा गया सवाल गलत था। यह योजना के चरण 1 की याद दिलाता है: "पूर्वी शाखा द्वारा बेचे गए पेन" दो शर्तें हैं, और केवल एक लिखी गई थी। फ़ाइल की एक नई कॉपी से फिर से शुरू करें और दोनों लिखें:

python
import pandas as pd

orders = pd.read_csv("orders.csv")

fix = (orders["branch"] == "east") & (orders["product"] == "pen")
print(fix.sum())

orders.loc[fix, "price"] = 13.5
print(orders.loc[orders["branch"] == "east", ["product", "price"]])
text
1
  product  price
3  bottle  120.0
6     pen   13.5

यहाँ दो आदतें दिखाई दे रही हैं। मास्क को एक नाम (fix) मिलता है क्योंकि इसका उपयोग किसी महत्वपूर्ण कार्य के लिए किया जाता है; और लिखने से पहले, fix.sum() बताता है कि कितनी पंक्तियाँ बदलने वाली हैं — एक, जो कि हमारी अपेक्षा थी। जब भी आप ओवरराइट करें, उससे पहले गिनती करें।

पढ़ने के लिए, df[mask]["col"] ठीक है। लिखने के लिए, हमेशा df.loc[mask, "col"] = value का उपयोग करें। चेन्ड रूप कोई त्रुटि नहीं देता, और परिवर्तन चुपचाप गायब हो जाता है।

काम करने के लिए फ़िल्टर की गई टेबल को सहेजना ठीक है

इसके बारे में क्या ख्याल है?

python
import pandas as pd

orders = pd.read_csv("orders.csv")

east = orders[orders["branch"] == "east"]
east["price"] = 0.0

print(east["price"].tolist())
print(orders["price"].tolist())
text
[0.0, 0.0]
[15.0, 60.0, 850.0, 120.0, 8.0, 850.0, 15.0, 120.0]

कोई चेतावनी नहीं, और यह सही है। यहाँ आपका इरादा एक अलग टेबल बनाने का था: आपने इसे east नाम दिया, और इसे बदलने से केवल east बदलता है। orders अछूता रहता है। कॉपी-ऑन-राइट इस व्यवहार को अनुमानित बनाता है — फ़िल्टर करके प्राप्त की गई तालिका हमेशा मूल से स्वतंत्र होती है। बग केवल चेन्ड रूप में होता है, जहाँ मध्यवर्ती तालिका का कोई नाम नहीं होता और परिवर्तन उसी के साथ गायब हो जाता है।


एक संपूर्ण उदाहरण

मैनेजर अब एक संक्षिप्त रिपोर्ट चाहता है। report.py:

python
import pandas as pd

orders = pd.read_csv("orders.csv")

# Check what arrived before asking it anything.
print("Rows, columns:", orders.shape)
print("Branches:", list(orders["branch"].unique()))
print()

# 1. The original question: north-branch orders of more than three items.
north_big = orders.loc[
    (orders["branch"] == "north") & (orders["quantity"] > 3),
    ["order_id", "product", "quantity"],
]
print("North orders over 3 items:")
print(north_big.reset_index(drop=True))
print()

# 2. Counting with masks: no new table needed.
accessories = orders["category"] == "accessories"
print("Accessory orders   :", accessories.sum())
print("Share of all orders:", accessories.mean())
print("Accessory items    :", orders.loc[accessories, "quantity"].sum())
print()

# 3. Outside north, cheap items (under 100).
cheap_outside = orders[
    (orders["branch"] != "north") & (orders["price"] < 100)
]
print("Cheap orders outside north:", cheap_outside["order_id"].tolist())

# 4. A branch we do not have: say so instead of printing an empty table.
west = orders[orders["branch"] == "west"]
if west.empty:
    print("No orders from the west branch.")
text
Rows, columns: (8, 7)
Branches: ['north', 'south', 'east']

North orders over 3 items:
   order_id product  quantity
0      1001     pen        12
1      1005  eraser        30
2      1008  bottle         4

Accessory orders   : 4
Share of all orders: 0.5
Accessory items    : 14

Cheap orders outside north: [1002, 1007]
No orders from the west branch.

यह इस तरह क्यों लिखा गया है:

  • पहले जाँचें, फिर पूछें। किसी भी फ़िल्टर से पहले shape और शाखाओं की सूची प्रिंट की जाती है। यदि फ़ाइल कैपिटलाइज़्ड North के साथ आती, तो खाली टेबल प्रिंट होने से पहले ही यह लाइन उसे पकड़ लेती।
  • मुख्य प्रश्न के लिए .loc[mask, columns]। उत्तर की पंक्तियों और कॉलमों का निर्णय एक ही स्थान पर किया जाता है, और reset_index(drop=True) परिणाम को पुन: क्रमांकित करता है क्योंकि यह अंतिम रिपोर्ट है, रास्ते का कोई मध्यवर्ती चरण नहीं।
  • एक नामित मास्क, जिसका तीन बार उपयोग किया गया। accessories की गणना एक बार की जाती है और फिर गिना जाता है (sum), मापा जाता है (mean) और एक कॉलम चुनने के लिए उपयोग किया जाता है। मास्क को नाम देकर आप एक ही शर्त को तीन बार लिखने और उनमें से किसी एक में गलती करने से बचते हैं।
  • प्रत्येक शर्त अपने स्वयं के कोष्ठक में, यहाँ तक कि छोटी शर्तों में भी, ताकि बाद में तीसरी शर्त जोड़ने पर प्राथमिकता की त्रुटि न हो।
  • खाली मामले को स्पष्ट रूप से संभाला गया है। शून्य पंक्तियाँ एक मान्य उत्तर है, और रिपोर्ट इसे शब्दों में कहती है।

समस्याएँ और समाधान

ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all(). आपने दो मास्क के बीच and, or या not का उपयोग किया — या पूरे मास्क पर if लगाया। पायथन के कीवर्ड्स को एक एकल True/False की आवश्यकता होती है, और एक मास्क में प्रति पंक्ति एक मान होता है। फ़िल्टर के अंदर, &, |, ~ लिखें। एक if के अंदर, स्पष्ट करें कि आपका क्या मतलब है: "क्या कम से कम एक पंक्ति सच है?" के लिए if mask.any():, या "क्या सभी पंक्तियाँ सच हैं?" के लिए if mask.all():।

TypeError: Cannot perform 'rand_' with a dtyped [float64] array and scalar of type [bool] (या 'ror_', या [int64]) & या | के बगल की शर्त के चारों ओर कोष्ठक गायब हैं। पायथन ने > से पहले & लागू किया, इसलिए उसने एक संख्या और एक कॉलम के बीच बिटवाइज़ "and" करने की कोशिश की। प्रत्येक शर्त को उसके अपने कोष्ठक में रखें: (a > 3) & (b < 100)।

फ़िल्टर बिना किसी त्रुटि के एक खाली तालिका लौटाता है आपका मान ठीक वैसे ही कॉलम में मौजूद नहीं है जैसा टाइप किया गया है। df["col"].unique() प्रिंट करें — या बेहतर है list(df["col"].unique()), जो प्रत्येक मान को उद्धरण चिह्नों में दिखाता है, ताकि 'north ' में पिछला स्पेस दिखाई दे जाए। df["col"].str.strip() के साथ डेटा को साफ़ करें, या .str.contains(..., case=False) के साथ लचीला मिलान करें।

फ़िल्टरिंग के ठीक बाद KeyError: 1 (या कोई अन्य संख्या) आपने फ़िल्टर की गई टेबल पर .loc[n] का उपयोग इस तरह किया जैसे उसमें अभी भी 0, 1, 2… लेबल हों। यह मूल लेबलों को बनाए रखता है। "n-वीं पंक्ति" के लिए .iloc[n] का उपयोग करें, या यदि पुराने लेबल अब उपयोगी नहीं हैं तो पहले reset_index(drop=True) करें।

ChainedAssignmentError: A value is being set on a copy of a DataFrame or Series through chained assignment. आपने df[mask]["col"] = value लिखा। यह पांडास 3 में df को कभी नहीं बदलता — चेतावनी आपको बता रही है कि इस लाइन ने कुछ नहीं किया। इसके स्थान पर df.loc[mask, "col"] = value लिखें।

UserWarning: Boolean Series key will be reindexed to match DataFrame index. मास्क उस टेबल के अलावा किसी अन्य टेबल से बनाया गया था जिसे आप फ़िल्टर कर रहे हैं — आमतौर पर पूर्ण orders से बना मास्क फ़िल्टर किए गए north पर उपयोग किया जाता है। पांडास इसे लेबल द्वारा संरेखित करता है और जो लेबल नहीं मिलते उन्हें चुपचाप छोड़ देता है:

python
import pandas as pd

orders = pd.read_csv("orders.csv")

north = orders[orders["branch"] == "north"]
big = orders["quantity"] > 5

print(north[big])
text
order_id        date branch product    category  quantity  price
0      1001  2024-03-01  north     pen  stationery        12   15.0
4      1005  2024-03-03  north  eraser  stationery        30    8.0
UserWarning: Boolean Series key will be reindexed to match DataFrame index.

(टर्मिनल में यह चेतावनी तालिका के ऊपर दिखाई देती है।) यहाँ संयोगवश सही उत्तर मिल जाता है, लेकिन केवल लेबलों के मेल खाने के कारण। हमेशा उसी टेबल से मास्क बनाएँ जिसे आप फ़िल्टर कर रहे हैं: north[north["quantity"] > 5]।