पंक्तियों को फ़िल्टर करना — हर पंक्ति से एक ही सवाल पूछना
केवल वही पंक्तियाँ रखना जो शर्त पूरी करती हैं: बूलियन मास्क, &, | और ~ से शर्तें जोड़ना, isin, between और .str मेथड्स, .loc से फ़िल्टर करना — और बिना किसी साइलेंट एरर के सीधे पंक्तियों का मान बदलना।
- 1समस्या
- 2समझें
- 3उदाहरण
- 4अनुमान
- 5स्वयं करें
- 6चुनौती
वह समस्या जिसे हम हल कर रहे हैं
दुकान के मैनेजर का एक संदेश आता है: "उत्तरी शाखा (north branch) के कौन से ऑर्डर तीन से अधिक वस्तुओं के थे? मैं उन्हें देखना चाहता हूँ।"
पिछले अध्याय के आठ ऑर्डरों के साथ आप केवल देखकर ही उत्तर दे सकते थे। अपनी उंगली branch कॉलम पर नीचे ले जाएँ, हर north पर रुकें, और पास के quantity कॉलम को देखें। तीन पंक्तियाँ। काम पूरा।
अब वास्तविक फ़ाइल की कल्पना करें — बारह हज़ार ऑर्डर, पूरे एक साल का डेटा। अब केवल देखकर काम नहीं चलेगा, और अधिकांश पायथन प्रोग्रामरों के दिमाग में जो पहला विचार आता है, वह एक लूप (loop) है:
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)[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 का उपयोग किया गया है। यदि आपके पास यह नहीं है, तो अपने प्रोजेक्ट फ़ोल्डर में ठीक इन नौ पंक्तियों के साथ इसे बनाएँ:
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), और वे सटीक मान जिनसे आप तुलना करेंगे।
import pandas as pd
orders = pd.read_csv("orders.csv")
print(orders.shape)
print(orders.dtypes)(8, 7)
order_id int64
date str
branch str
product str
category str
quantity int64
price float64
dtype: objectquantity कॉलम का प्रकार int64 है, इसलिए > 3 संख्याओं की तुलना करेगा। यदि यह str के रूप में आया होता — क्योंकि किसी ने एक सेल में "3 pcs" टाइप कर दिया था — तो तुलना TypeError के साथ विफल हो जाती; और आप यह बात अभी जानना चाहते हैं, काम के बीच में नहीं। branch कॉलम str है, इसलिए इसकी तुलना टेक्स्ट के रूप में की जाएगी, और टेक्स्ट की तुलना बिल्कुल सटीक होती है: "north", "North" और "north " तीन अलग-अलग मान हैं। इसलिए कॉलम में वास्तव में मौजूद मानों को देखें:
print(orders["branch"].unique())<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 नहीं देता। यह प्रत्येक पंक्ति की तुलना करता है और आपको उत्तरों का एक पूरा कॉलम देता है:
big = orders["quantity"] > 5
print(big)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 है:
print(orders[big])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 से ही बनाया गया था, इसलिए यह अंतर दिखाई नहीं देता। यह केवल तब दिखाई देता है जब आप किसी भिन्न टेबल से बनाए गए मास्क का उपयोग करते हैं — जिसे "समस्याएँ और समाधान" में समझाया गया है।
आप आमतौर पर मास्क को बिना किसी अलग नाम के सीधे लिखा हुआ देखेंगे:
print(orders[orders["branch"] == "north"])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 गिना जाता है, इसलिए:
print(big.sum())
print(big.mean())4
0.5sum() सभी एक (ones) को जोड़ता है: 4 ऑर्डर। mean() पंक्तियों की कुल संख्या से भाग देता है: 8 में से 4 यानी 0.5, जिसका अर्थ है कि आधे ऑर्डर पाँच से अधिक वस्तुओं के थे। "कितने" के लिए मास्क का .sum() निकालें; "कितना हिस्सा या प्रतिशत" के लिए मास्क का .mean() निकालें। दोनों एक ही पंक्ति के काम हैं और दोनों में से कोई भी नई टेबल नहीं बनाता।
और जब आप फ़िल्टर की गई पंक्तियों के किसी कॉलम का कुल योग चाहते हैं — उन बड़े ऑर्डरों में वस्तुओं की कुल संख्या — तो पहले फ़िल्टर करें, फिर कॉलम चुनें, फिर योग करें:
print(orders[big]["quantity"].sum())6912 + 7 + 30 + 20 = 69। फ़िल्टर की गई टेबल के किसी कॉलम को इस तरह पढ़ना (read) पूरी तरह सुरक्षित है। लेकिन इस तरह से उसमें मान लिखना (write) गलत है — वह इस अध्याय का अंतिम भाग है।
कई शर्तें: &, |, ~
मैनेजर के प्रश्न में दो शर्तें हैं। पांडास तीन ऑपरेटरों के साथ मास्क को जोड़ता है:
&का अर्थ है and — पंक्ति तब रखी जाती है जब दोनों मास्कTrueहों|का अर्थ है or — पंक्ति तब रखी जाती है जब कम से कम एक मास्कTrueहो~का अर्थ है not — यह प्रत्येकTrueकोFalseमें औरFalseकोTrueमें बदल देता है
यहाँ अंततः मैनेजर का प्रश्न हल किया गया है:
north_big = orders[(orders["branch"] == "north") & (orders["quantity"] > 3)]
print(north_big)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 से जाँच:
print(len(north_big))
print((north_big["quantity"] > 3).all())
print((north_big["branch"] == "north").all())3
True
Trueपंक्तियों की संख्या मेल खाती है, और बची हुई प्रत्येक पंक्ति में दोनों शर्तें लागू होती हैं। .all() पूछता है "क्या इस मास्क का प्रत्येक मान True है?" — यह साबित करने का सबसे तेज़ तरीका है कि फ़िल्टर ने वही किया जो आप चाहते थे।
एक or (या) प्रश्न: पूर्वी शाखा के ऑर्डर, या 850 या उससे अधिक कीमत वाले किसी भी उत्पाद का कोई भी ऑर्डर।
print(orders[(orders["branch"] == "east") | (orders["price"] >= 850)])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) को छोड़कर बाकी सब कुछ।
print(orders[~(orders["category"] == "stationery")])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 को नकार रहे हों (जिसे आप जल्द ही देखेंगे)।
कोष्ठक लगाना क्यों अनिवार्य है
ऊपर दी गई प्रत्येक शर्त अपने स्वयं के कोष्ठक के अंदर है। यह कोई कोडिंग स्टाइल नहीं है। उन्हें हटा दें और कोड टूट जाएगा:
print(orders[orders["quantity"] > 3 & orders["price"] < 100])TypeError: Cannot perform 'rand_' with a dtyped [float64] array and scalar of type [bool]ऊपर जो आप देख रहे हैं वह ट्रेसबैक की अंतिम पंक्ति है, वह पंक्ति जो त्रुटि का नाम बताती है; इसके ऊपर की पंक्तियाँ केवल यह बताती हैं कि यह कहाँ हुआ। इसका कारण ऑपरेटर प्राथमिकता (operator precedence) है — यानी वह क्रम जिसमें पायथन ऑपरेटरों को लागू करता है। पायथन में, & की प्राथमिकता > और < से अधिक होती है। यह पांडास के अस्तित्व में आने से बहुत पहले तय किया गया था, क्योंकि & पूर्ण संख्याओं (बिटवाइज़ "and") के लिए डिज़ाइन किया गया था, जहाँ इसे पहले होना चाहिए। इसलिए पायथन इस पंक्ति को इस प्रकार पढ़ता है:
orders["quantity"] > (3 & orders["price"]) < 100यह पहले 3 & orders["price"] की गणना करने का प्रयास करता है — यानी संख्या 3 और दशमलव संख्याओं के कॉलम के बीच एक बिटवाइज़ "and" — और यही कारण है कि यह त्रुटि आती है। संदेश में rand_ (दाहिने हाथ का "and") और एक ऐरे का उल्लेख होता है, लेकिन कारण हमेशा एक ही होता है: कोष्ठक का छूटना।
त्रुटि संदेश आना वास्तव में अच्छी बात है। यदि आप केवल पहली शर्त को कोष्ठक में लपेटते हैं और दूसरी को भूल जाते हैं, तो कोई एरर नहीं आता — और वही असली खतरा है:
print(len(orders[(orders["quantity"] > 3) & orders["price"] < 100]))
print(len(orders[(orders["quantity"] > 3) & (orders["price"] < 100)]))8
4पहली पंक्ति को ((quantity > 3) & price) < 100 के रूप में पढ़ा जाता है। & मास्क को कीमतों के साथ मिला देता है, और संयोग से हर परिणाम 100 से कम होता है, इसलिए यह "फ़िल्टर" सभी आठ पंक्तियों को रख लेता है। जबकि सही फ़िल्टर चार पंक्तियों को रखता है। वही कॉलम, वही संख्याएँ, बस कोष्ठक का एक जोड़ा गायब — और गलत संस्करण बिना किसी आवाज़ के चल जाता है। हर बार, हर शर्त को उसके अपने कोष्ठक में रखें। तब न तो कोई त्रुटि हो सकती है और न ही कोई शांत विफलता।
and का उपयोग क्यों नहीं?
स्वाभाविक प्रवृत्ति अंग्रेज़ी शब्द लिखने की होती है:
print(orders[(orders["branch"] == "north") and (orders["quantity"] > 3)])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 एक सूची लेता है और पूछता है "क्या इस पंक्ति का मान सूची में है?":
print(orders[orders["branch"].isin(["east", "south"])])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 के आगे ~ लगाना:
print(orders[~orders["branch"].isin(["north", "east"])]["order_id"].tolist())[1002, 1006]दो ऑर्डर, दोनों दक्षिणी शाखा से। ध्यान दें कि ~orders["branch"].isin([...]) को किसी अतिरिक्त कोष्ठक की आवश्यकता नहीं है: ~ विधि कॉल के परिणाम पर लागू होता है, और यहाँ संघर्ष करने के लिए कोई & या | नहीं है। जैसे ही आप इसे किसी अन्य शर्त के साथ जोड़ते हैं, कोष्ठक वापस आ जाते हैं: (~orders["branch"].isin([...])) & (orders["quantity"] > 3)।
एक सीमा (range): between
15 से 120 मूल्य वाले ऑर्डर। यह दो तुलनाएँ हैं, >= 15 और <= 120। between इसे एक में कह देता है:
print(orders[orders["price"].between(15, 120)])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.0between डिफ़ॉल्ट रूप से दोनों सिरों को शामिल करता है — ठीक 15.0 पर पेन और ठीक 120.0 पर बोतलें दोनों इसमें शामिल हैं। यह इस बात से मेल खाता है कि लोग आमतौर पर "15 से 120" कैसे कहते हैं, लेकिन प्रश्न के संदर्भ में इसकी पुष्टि कर लें। यदि प्रश्न का अर्थ स्पष्ट रूप से बीच का है, तो वैसा निर्दिष्ट करें:
print(orders["price"].between(15, 120, inclusive="neither").sum())1केवल 60.0 वाली नोटबुक ही विशेष रूप से बीच में है। inclusive आधे-खुले रेंज के लिए "left" और "right" भी स्वीकार करता है — जो पैसे के स्लैब के लिए सामान्य विकल्प है, जहाँ आप सीमा मान को दो श्रेणियों में गिने बिना 0–100, 100–200 आदि चाहते हैं।
टेक्स्ट पैटर्न: .str विधियाँ
टेक्स्ट पर सामान्य समानता सटीक होती है। .str एक्सेसॉर टेक्स्ट कॉलम को पायथन की जानी-पहचानी स्ट्रिंग विधियाँ प्रदान करता है, जो प्रत्येक पंक्ति पर लागू होती हैं, और प्रत्येक एक मास्क लौटाती है।
वे उत्पाद जिनका नाम "b" से शुरू होता है:
print(orders[orders["product"].str.startswith("b")])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" अक्षर शामिल है, चाहे वह किसी भी केस में हो:
print(orders[orders["product"].str.contains("O", case=False)])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") को कुछ नहीं मिलता।
सटीक समानता में ऐसा कोई विकल्प नहीं होता है। यह सबसे आम "मेरा फ़िल्टर कुछ भी नहीं लौटाता" वाला बग है:
print(orders[orders["branch"] == "North"])Empty DataFrame
Columns: [order_id, date, branch, product, category, quantity, price]
Index: []कोई एरर नहीं, कोई पंक्ति नहीं। टेबल गलत नहीं है और पांडास भी गलत नहीं है — बस कैपिटल लेटर में लिखा "North" नाम का कोई ब्रांच ही नहीं है। यही कारण है कि योजना के चरण 2 में unique() प्रिंट किया गया था: जिन मानों से आप तुलना करते हैं उन्हें कॉलम में वास्तव में जो है उससे कॉपी किया जाना चाहिए, न कि याददाश्त से टाइप किया जाना चाहिए।
टेक्स्ट तारीखें सही ढंग से तुलना करती हैं — जब वे ISO प्रारूप में हों
date कॉलम str के रूप में आया। क्या आप इसे >= से फ़िल्टर कर सकते हैं? 3 मार्च से आगे के ऑर्डर:
print(orders["date"] >= "2024-03-03")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] देखा था। पंक्तियों वाले हिस्से में मास्क भी स्वीकार किया जाता है:
print(orders.loc[orders["branch"] == "north", ["product", "quantity"]])product quantity
0 pen 12
2 bag 2
4 eraser 30
7 bottle 4एक चरण, एक ऑब्जेक्ट, और आपके कोड का पाठक पूरे प्रश्न को — कौन सी पंक्तियाँ, कौन से कॉलम — एक ही स्थान पर देखता है। .loc[mask, "quantity"] (एकल कॉलम का नाम, सूची नहीं) आपको इसके बजाय एक Series देता है, जो अंकगणित के लिए तैयार है:
print(orders.loc[orders["branch"] == "north", "quantity"].sum())4812 + 2 + 30 + 4 = उत्तरी शाखा द्वारा बेची गई 48 वस्तुएं।
query(): वाक्य के रूप में वही फ़िल्टर
फ़िल्टर के लिए एक दूसरा तरीका query() है, जो शर्त को एक स्ट्रिंग के रूप में लेता है:
print(orders.query("branch == 'north' and quantity > 3"))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 स्ट्रिंग को पायथन के ऑपरेटरों को सौंपने के बजाय स्वयं पार्स करता है। किसी पायथन वेरिएबल का उपयोग करने के लिए, उसके पहले @ लगाएं:
min_qty = 5
print(orders.query("quantity >= @min_qty")["order_id"].tolist())[1001, 1002, 1004, 1005, 1007]लंबी शर्तों के लिए query पढ़ने में बहुत अच्छा लगता है। इसकी कमियाँ: स्ट्रिंग के अंदर टाइपो का पता केवल लाइन चलने पर ही चलता है, आपका एडिटर इसके अंदर आपकी मदद नहीं कर सकता, और स्पेस वाले कॉलम नामों के लिए बैकबिक्स की आवश्यकता होती है। यह पाठ्यक्रम मास्क को मुख्य टूल के रूप में उपयोग करता है, क्योंकि मास्क वही हैं जिन पर पांडास में बाकी सब कुछ — .loc, असाइनमेंट, गिनती — बना है। query को जानें ताकि आप इसे अन्य लोगों के कोड में पढ़ सकें।
फ़िल्टर की गई टेबल अपने पुराने लेबल बनाए रखती है
इंडेक्स पर वापस आते हैं। उत्तरी शाखा के ऑर्डर सहेजें और उन्हें देखें:
north = orders[orders["branch"] == "north"]
print(north.index.tolist())[0, 2, 4, 7]लेबल 0, 2, 4, 7 हैं। तो north.loc[1] क्या होगा? इस टेबल में कोई लेबल 1 नहीं है — ऑर्डर 1002 दक्षिणी शाखा से आया था और फ़िल्टर हो गया था:
print(north.loc[1])KeyError: 1.loc एक लेबल खोजता है, और लेबल 1 यहाँ मौजूद नहीं है। यदि आपका मतलब "दूसरा उत्तरी शाखा का ऑर्डर" था, तो वह एक स्थिति (position) है, और स्थितियाँ .iloc होती हैं:
print(north.iloc[1])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 का अंतर है, और फ़िल्टरिंग वह जगह है जहाँ यह सामने आता है, क्योंकि फ़िल्टर के बाद लेबल और स्थितियाँ मेल खाना बंद कर देती हैं।
लेबल बनाए रखना जानबूझकर किया गया डिज़ाइन है: यह आपको परिणाम की किसी भी पंक्ति को मूल टेबल से जोड़ने की अनुमति देता है। जब आपको इसकी आवश्यकता न हो — जैसे कि फ़िल्टर की गई टेबल अंतिम रिपोर्ट हो — तो इसे फिर से नंबर दें:
print(north.reset_index(drop=True))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.0drop=True पुराने लेबलों को हटा देता है। इसके बिना, उन्हें index नामक एक नए कॉलम के रूप में रखा जाएगा, जो कि रिपोर्ट में आमतौर पर आवश्यक नहीं होता है।
खाली परिणाम कोई त्रुटि नहीं है
none = orders[orders["branch"] == "west"]
print(none)
print(none.shape)
print(none.empty)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 होना चाहिए था। लुभावना कोड पहले फ़िल्टर करता है और फिर कॉलम चुनता है — कोष्ठक के दो जोड़े, जिस तरह से आपने पहले मान पढ़े थे:
import pandas as pd
orders = pd.read_csv("orders.csv")
orders[orders["branch"] == "east"]["price"] = 13.5
print(orders["price"].tolist())[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], जिस पर असाइनमेंट किया जाए:
orders.loc[orders["branch"] == "east", "price"] = 13.5
print(orders["price"].tolist())[15.0, 60.0, 850.0, 13.5, 8.0, 850.0, 13.5, 120.0]रुकिए — इसने पूर्वी शाखा के दोनों ऑर्डरों, पेन और बोतल, को बदल दिया। बोतल की कीमत तो गलत नहीं थी। कोड ने वही किया जो लिखा गया था; कोड में पूछा गया सवाल गलत था। यह योजना के चरण 1 की याद दिलाता है: "पूर्वी शाखा द्वारा बेचे गए पेन" दो शर्तें हैं, और केवल एक लिखी गई थी। फ़ाइल की एक नई कॉपी से फिर से शुरू करें और दोनों लिखें:
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"]])1
product price
3 bottle 120.0
6 pen 13.5यहाँ दो आदतें दिखाई दे रही हैं। मास्क को एक नाम (fix) मिलता है क्योंकि इसका उपयोग किसी महत्वपूर्ण कार्य के लिए किया जाता है; और लिखने से पहले, fix.sum() बताता है कि कितनी पंक्तियाँ बदलने वाली हैं — एक, जो कि हमारी अपेक्षा थी। जब भी आप ओवरराइट करें, उससे पहले गिनती करें।
पढ़ने के लिए,df[mask]["col"]ठीक है। लिखने के लिए, हमेशाdf.loc[mask, "col"] = valueका उपयोग करें। चेन्ड रूप कोई त्रुटि नहीं देता, और परिवर्तन चुपचाप गायब हो जाता है।
काम करने के लिए फ़िल्टर की गई टेबल को सहेजना ठीक है
इसके बारे में क्या ख्याल है?
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())[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:
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.")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 पर उपयोग किया जाता है। पांडास इसे लेबल द्वारा संरेखित करता है और जो लेबल नहीं मिलते उन्हें चुपचाप छोड़ देता है:
import pandas as pd
orders = pd.read_csv("orders.csv")
north = orders[orders["branch"] == "north"]
big = orders["quantity"] > 5
print(north[big])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]।
चरण 4 / 6 — अनुमान
अपनी समझ की जाँच करें
क्या प्रिंट होगा?
from io import StringIO
import pandas as pd
# Stands in for orders.csv, so this snippet runs on its own.
RAW = """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
"""
orders = pd.read_csv(StringIO(RAW))
mask = orders["quantity"] > 5
print(mask.sum(), len(orders[mask]))- A4 8
- B5 5
- C4 4
- DTrue 4
इसका उद्देश्य north शाखा के तीन से अधिक आइटम वाले ऑर्डर रखना है। इसे चलाने पर क्या होगा?
from io import StringIO
import pandas as pd
# Stands in for orders.csv, so this snippet runs on its own.
RAW = """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
"""
orders = pd.read_csv(StringIO(RAW))
north_big = orders[orders["branch"] == "north" & orders["quantity"] > 3]- Aयह सही ढंग से काम करेगा और ऑर्डर 1001, 1005 और 1008 को रखेगा
- Bयह एक खाली टेबल लौटाएगा
- Cएक `TypeError`: कोष्ठक के बिना, `&` ऑपरेटर `==` और `>` से पहले लागू हो जाता है
- Dएक `ValueError: The truth value of a Series is ambiguous`
आपको सीधे orders टेबल में प्रत्येक bag की कीमत आधी करनी है। कौन सी लाइन यह काम सही ढंग से करेगी?
- Aorders[orders["product"] == "bag"]["price"] = orders["price"] / 2
- Borders.loc[orders["product"] == "bag", "price"] = orders["price"] / 2
- Cbags = orders[orders["product"] == "bag"] bags["price"] = bags["price"] / 2
- Dorders["price"][orders["product"] == "bag"] /= 2
उत्तर देने के लिए अकाउंट आवश्यक है
अपने उत्तर जाँचने के लिए साइन इन करें
प्रश्न ऊपर दिए गए हैं, और मन में उत्तर सोचना ही मुख्य कार्य है। सही उत्तर, व्याख्या और तीन-स्तरीय संकेत देखने के लिए साइन इन करें।
आपकी बारी
उसी orders.csv का उपयोग करें। filters.py लिखें जो चार प्रश्नों के उत्तर देता है, प्रत्येक को एक छोटे शीर्षक के तहत प्रिंट किया जाए:
- कितने ऑर्डर 10 या उससे अधिक वस्तुओं के लिए थे? एक संख्या प्रिंट करें, टेबल नहीं।
- उत्तरी शाखा के बाहर के एक्सेसरी ऑर्डर — केवल
order_id,branch,productऔरprice, जिन्हें 0 से पुन: क्रमांकित किया गया हो। - दक्षिणी या पूर्वी शाखा के ऑर्डर जिनका मूल्य 100 से कम है। शाखाओं के लिए
isinका उपयोग करें। - मुख्य कार्यालय 20 या अधिक वस्तुओं के प्रत्येक स्टेशनरी ऑर्डर पर 10% की छूट देता है। ठीक उन पंक्तियों की
priceको उनके वर्तमान मूल्य के 90% पर सेट करें, लेकिन पहले प्रिंट करें कि आप कितनी पंक्तियाँ बदलने वाले हैं। फिर सभी स्टेशनरी ऑर्डरों के लिएorder_id,quantityऔरpriceप्रिंट करें।
कोई भी कोड लिखने से पहले, कागज़ पर प्रत्येक प्रश्न की योजना बनाएं: कौन से कॉलम, कौन सी तुलनाएँ, किससे जुड़ी हैं, और फ़ाइल देखकर आप किन पंक्तियों की उम्मीद करते हैं। जब आपका प्रोग्राम सही होगा, तो यह बिल्कुल यह प्रिंट करेगा:
1. Orders of 10+ items:
3
2. Accessory orders outside north:
order_id branch product price
0 1004 east bottle 120.0
1 1006 south bag 850.0
3. South or east, price under 100:
order_id date branch product category quantity price
1 1002 2024-03-01 south notebook stationery 5 60.0
6 1007 2024-03-04 east pen stationery 20 15.0
4. Stationery discount:
Rows to change: 2
order_id quantity price
0 1001 12 15.0
1 1002 5 60.0
4 1005 30 7.2
6 1007 20 13.5फिर जानबूझकर इसे एक-एक बार तोड़कर देखें:
- प्रश्न 3 में कोष्ठक हटा दें। कौन सी त्रुटि दिखाई देती है?
- प्रश्न 4 को
orders[mask]["price"] = ...के रूप में लिखें। पांडास क्या प्रिंट करता है, और क्या कोई कीमत बदली? - प्रश्न 3 में
"east"को बदलकर"East"करें। क्या होता है, और यह किसी एरर से भी बदतर क्यों है?
समाधान
पहले योजना बनाएं। प्रत्येक प्रश्न को अलग करें:
quantity >= 10, एक शर्त। देखकर: 12, 30 और 20, यानी 3।category == "accessories"तथाbranch != "north"। देखकर: 1004 (पूर्व, बोतल) और 1006 (दक्षिण, बैग)।branchदक्षिण/पूर्व में तथाprice < 100। देखकर: 1002 (नोटबुक, 60.0) और 1007 (पेन, 15.0)।category == "stationery"तथाquantity >= 20। देखकर: 1005 (इरेज़र, 30) और 1007 (पेन, 20), यानी 2 पंक्तियाँ।
प्रश्न 4 में सावधानी की आवश्यकता है: "20 या अधिक वस्तुएं" का अर्थ है >= 20, इसलिए ठीक 20 वस्तुओं वाला ऑर्डर 1007 भी शामिल है। और चूँकि यह मान लिखता है, यह एक नामित मास्क के साथ .loc[mask, "price"] का उपयोग करता है, जिसे पहले गिना जाता है।
filters.py:
import pandas as pd
orders = pd.read_csv("orders.csv")
print("1. Orders of 10+ items:")
print((orders["quantity"] >= 10).sum())
print()
print("2. Accessory orders outside north:")
outside = orders.loc[
(orders["category"] == "accessories") & (orders["branch"] != "north"),
["order_id", "branch", "product", "price"],
]
print(outside.reset_index(drop=True))
print()
print("3. South or east, price under 100:")
cheap = orders[
(orders["branch"].isin(["south", "east"])) & (orders["price"] < 100)
]
print(cheap)
print()
print("4. Stationery discount:")
discount = (orders["category"] == "stationery") & (orders["quantity"] >= 20)
print("Rows to change:", discount.sum())
orders.loc[discount, "price"] = orders.loc[discount, "price"] * 0.9
print(orders.loc[orders["category"] == "stationery", ["order_id", "quantity", "price"]])1. Orders of 10+ items:
3
2. Accessory orders outside north:
order_id branch product price
0 1004 east bottle 120.0
1 1006 south bag 850.0
3. South or east, price under 100:
order_id date branch product category quantity price
1 1002 2024-03-01 south notebook stationery 5 60.0
6 1007 2024-03-04 east pen stationery 20 15.0
4. Stationery discount:
Rows to change: 2
order_id quantity price
0 1001 12 15.0
1 1002 5 60.0
4 1005 30 7.2
6 1007 20 13.5प्रत्येक उत्तर योजना से मेल खाता है। विकल्पों पर नोट्स:
- प्रश्न 1 मास्क का
sum()है,len(orders[...])नहीं। दोनों 3 देते हैं, लेकिन मास्क सीधे गिनती करता है और कोई तालिका नहीं बनाता। - प्रश्न 2
!=का उपयोग करता है "उत्तरी शाखा के बाहर" के लिए।~(orders["branch"] == "north")का अर्थ वही है; एक मान के लिए!=सरल है। - प्रश्न 3 मूल लेबल
1और6को बनाए रखता है, क्योंकि कार्य ने पुन: संख्या देने के लिए नहीं कहा था। वे लेबल आपको बताते हैं कि वेordersकी कौन सी पंक्तियाँ थीं। - प्रश्न 4 लिखने से पहले गिनती करता है, और नए मान की गणना दाईं ओर की उन्हीं पंक्तियों से की जाती है:
orders.loc[discount, "price"] * 0.9। दोनों पक्ष एक ही मास्क का उपयोग करते हैं, इसलिए प्रत्येक पंक्ति को अपनी कीमत का 90% मिलता है: 8.0 बदलकर 7.2 हो जाता है, 15.0 बदलकर 13.5 हो जाता है। पेन ऑर्डर1001(12 वस्तुएं) और नोटबुक (5 वस्तुएं) अपनी पुरानी कीमतें बनाए रखते हैं।
और तीन गलतियों का विश्लेषण:
- कोष्ठक के बिना, प्रश्न 3
TypeError: Cannot perform 'rand_' …उठाता है, क्योंकि&को<से पहले लागू किया गया था। orders[discount]["price"] = …ChainedAssignmentErrorचेतावनी प्रिंट करता है, स्क्रिप्ट चलती रहती है, और अंतिम तालिका अभी भी पुरानी कीमतें8.0और15.0दिखाती है। इस लाइन ने कुछ नहीं किया।"East"के साथ, प्रश्न 3 चुपचाप पूर्वी शाखा के पेन को खो देता है और दो के बजाय एक पंक्ति प्रिंट करता है। कोई एरर नहीं, कोई चेतावनी नहीं: सिर्फ़ एक गलत उत्तर जो सही दिखता है। यही कारण है कि योजना कोड चलने से पहले अपेक्षित पंक्तियों को लिखती है। केवल वही व्यक्ति इस बग को पकड़ सकता है जिसे पहले से पता था कि उत्तर में दो पंक्तियाँ होनी चाहिए।
Step 6 of 6
चुनौती — the chapter quiz
सरल से कठिन — दस प्रश्न, अंतिम वाले जानबूझकर चुनौतीपूर्ण बनाए गए हैं।
Sign in to take the quiz