2025, Sep 29 21:31
pandas resample में min_count और per-column agg को नियंत्रित करने के भरोसेमंद तरीके
pandas time series में resample के दौरान min_count, agg और dict-आधारित एग्रीगेशन को नियंत्रित करें: खाली बकेट्स में None बनाम 0, और concat के व्यावहारिक तरीके।
pandas में समय‑श्रृंखला (time series) का resample करना अक्सर सीधा लगता है, जब तक कि आपको हर कॉलम के लिए एग्रीगेशन के व्यवहार पर अलग‑अलग नियंत्रण न चाहिए। एक आम स्थिति: आप resample के दौरान sum चाहते हैं, लेकिन जिस बकेट में कोई पंक्तियाँ नहीं आईं, वहाँ 0 की बजाय None/NaN चाहिए। एक अकेले एग्रीगेशन के साथ यह सरल है; मुश्किल जैसे ही आप एग्रीगेशनों की डिक्शनरी पर स्विच करते हैं, सामने आ जाती है।
समस्या की रूपरेखा
एक न्यूनतम इनपुट लें जिसमें केवल टाइमस्टैम्प्ड इंडेक्स और एक कॉलम हो। sum के साथ resample मांगने पर और min_count=1 देने पर खाली विंडो के लिए अपेक्षित None मिलता है:
import pandas as pd
data_tbl = pd.DataFrame(index=[pd.to_datetime('2020-01-01')],
                        columns=['metric'])
ok_out = data_tbl.resample('5min').agg('sum', min_count=1)
परिणाम:
           metric
2020-01-01    None
लेकिन जैसे ही आप per-column डिक्शनरी पर जाते हैं, min_count की अर्थवत्ता खो जाती है और 0 लौटता है:
bad_out = data_tbl.resample('5min').agg({'metric': 'sum'}, min_count=1)
परिणाम:
           metric
2020-01-01       0
असल में क्या होता है और क्यों
min_count जैसे keyword arguments को स्ट्रिंग एग्रीगेटर के साथ पास करना काम करता है क्योंकि वे Resampler.sum के सम्बंधित इम्प्लिमेंटेशन तक फॉरवर्ड कर दिए जाते हैं। लेकिन जैसे ही आप agg को एक डिक्शनरी देते हैं, pandas उन kwargs को प्रत्येक कुंजी के हिसाब से फॉरवर्ड नहीं करता। यानी आप dict mapping का उपयोग करते हुए आधारभूत एग्रीगेशन फ़ंक्शनों को अलग‑अलग kwargs वैसे पास नहीं कर सकते जैसा आप सोच सकते हैं। यह व्यवहार फिलहाल समर्थित नहीं है; agg के लिए भी एक समान समस्या रिपोर्ट की गई है/थी।
व्यावहारिक तरीके
जब resampling के दौरान आपको kwargs पर नियंत्रण चाहिए, तो कुछ पैटर्न ऐसे हैं जो बिना असमर्थित kwargs‑forwarding पर निर्भर हुए भरोसेमंद तरीके से सही व्यवहार बनाए रखते हैं।
यदि कई कॉलम्स के लिए एक ही एग्रीगेशन चाहिए, तो agg कॉल करने से पहले उन कॉलम्स का slice लें। इससे min_count=1 इच्छित रूप से काम करता है:
data_tbl = pd.DataFrame(index=[pd.to_datetime('2020-01-01')],
                        columns=['metric', 'metric2', 'metric3'])
same_res = data_tbl.resample('5min')[['metric', 'metric2']].agg('sum', min_count=1)
परिणाम:
           metric metric2
2020-01-01   None    None
यदि हर कॉलम के लिए अलग एग्रीगेशन चाहिए, तो concat से परिणाम जोड़ें। एक resampler बनाएं, कॉलम‑वाइज एग्रीगेशन्स उनके kwargs के साथ लगाएँ, और कॉलम‑अक्ष पर concatenate करें:
agg_plan = {'metric': 'sum', 'metric2': 'min'}
rs_obj = data_tbl.resample('5min')
mixed_res = pd.concat({col: rs_obj[col].agg([fn], min_count=1)
                       for col, fn in agg_plan.items()}, axis=1)
परिणाम:
           metric metric2
              sum     min
2020-01-01   None     NaN
यदि फ़ंक्शन भी कॉलम‑दर‑कॉलम बदलते हों और उनके kwargs भी, तो per-column kwargs मैपिंग रखें और concat के दौरान लागू करें:
agg_plan = {'metric': 'sum', 'metric2': 'min'}
kw_per_col = {'metric2': {'min_count': 1}}
rs_obj = data_tbl.resample('5min')
varkw_res = pd.concat({col: rs_obj[col].agg([fn], **kw_per_col.get(col, {}))
                       for col, fn in agg_plan.items()}, axis=1)
परिणाम:
           metric metric2
              sum     min
2020-01-01      0     NaN
आखिरी मामले में, केवल metric2 को min_count=1 मिला, इसलिए metric खाली अवधि में 0 देने वाले डिफ़ॉल्ट sum व्यवहार पर लौट आया।
यह क्यों मायने रखता है
Zero बनाम None कोई सौंदर्यगत फर्क नहीं है। टाइम‑सीरीज़ पाइपलाइनों में डाउनस्ट्रीम लॉजिक—ratios, rolling stats, anomaly flags—इस बात पर अलग तरह से व्यवहार करता है कि खाली विंडो को no data माना गया या वास्तविक शून्य। पूर्वानुमेय एग्रीगेशन‑सेमांटिक्स बनाए रखने से मेट्रिक्स में चुपचाप होने वाली ड्रीफ्ट से बचाव होता है।
यह प्रदर्शन (performance) के लिहाज़ से भी ध्यान देने योग्य है। एग्रीगेशनों की डिक्शनरी का उपयोग पहले से ही single‑aggregator कॉल की तुलना में ओवरहेड जोड़ता है। 10K पंक्तियों और 3 कॉलम वाले एक प्रतिनिधिक इनपुट पर, और resampler पहले से प्रीकम्प्यूट होने पर, r.agg('sum') लगभग 402 μs ± 40.6 μs मापा गया; डिक्शनरी रूप r.agg({'value': 'sum', 'value2': 'sum', 'value3': 'sum'}) लगभग 1.46 ms ± 112 μs; जबकि concat वाला तरीका लगभग 2.15 ms ± 60.4 μs रहा। जब आपको per-column नियंत्रण चाहिए, तो सापेक्ष लागत का यह अंदाज़ा उपयोगी है।
मुख्य निष्कर्ष
यदि कई कॉलम्स पर एक ही एग्रीगेशन और वही kwargs चाहिए, तो उन्हें एक स्लाइस्ड व्यू पर एक ही agg कॉल में समूहित करें; कोड कॉम्पैक्ट रहेगा और अतिरिक्त concatenation से बचेगा। जब एग्रीगेशन्स या kwargs कॉलम‑दर‑कॉलम अलग हों, तो परिणाम कॉलम‑वाइज बनाकर concatenate करें। single‑aggregator कॉल की तुलना में प्रदर्शन‑परिणामों के लिए तैयार रहें; dict‑आधारित तरीका पहले ही महँगा है, और concat पैटर्न उस पर थोड़ा और जोड़ देता है। यदि प्रदर्शन निर्णायक हो, तो अपने वर्कलोड का पुनरुत्पाद्य स्लाइस तैयार करें और प्रत्येक विकल्प को अपने परिवेश में नापें।
जब तक dict‑आधारित agg के लिए kwargs forwarding समर्थित नहीं हो जाता, ये पैटर्न शुद्धता से समझौता किए बिना resample व्यवहार पर स्पष्ट और पूर्वानुमेय नियंत्रण देते हैं।
यह लेख StackOverflow के प्रश्न (लेखक: KamiKimi 3) और mozway के उत्तर पर आधारित है।