2025, Oct 06 23:33
Polars में crosstab को प्रतिशत में बदलने के दो तरीके: जनरेटर अनपैक और नैटिव एक्सप्रेशन
Polars में crosstab काउंट्स को प्रतिशत में बदलें: select में जनरेटर अनपैक क्यों जरूरी है, और sum_horizontal व with_columns से नैटिव, लेज़ी-फ्रेंडली समाधान पाएं.
Polars में क्रॉसटैब को प्रतिशत हिस्सों में बदलना सतह पर आसान लगता है, लेकिन एक छोटी-सी बारीकी रोड़ा बन जाती है: select में अभिव्यक्ति-जनरेटर को अन्य आर्ग्युमेंट्स के साथ जोड़ना। नीचे समस्या का संक्षिप्त विवरण है—त्रुटि क्यों आती है, और इसे सुलझाने के दो साफ तरीके: या तो जनरेटर को अनपैक करें, या फिर ऐसा नैटिव Polars एक्सप्रेशन इस्तेमाल करें जो चलते-चलते ग्रैंड टोटल निकाल दे।
समस्या का सेटअप
लक्ष्य एक ऐसी क्रॉस टेबल बनाना है, जहाँ हर मान कुल पंक्तियों का प्रतिशत दिखाए। पहले ऐसे पिवट से शुरुआत करें जो मानों की गिनती देता है, और फिर इन काउंट्स को प्रतिशत में बदलें।
import polars as pl
import polars.selectors as cs
# स्रोत डेटा
frame_src = pl.DataFrame({"a": [2, 0, 1, 0, 0, 0], "b": [1, 1, 1, 0, 0, 1]})
# गिनती वाली क्रॉस टेबल
xtab = (
    frame_src
    .pivot(on="b", index="a", values="b", aggregate_function="len", sort_columns=True)
    .fill_null(0)
    .sort("a")
)
# प्रतिशत अभिव्यक्तियों का जनरेटर
def pct_exprs(tbl):
    total_all = tbl.select(~cs.by_index(0)).to_numpy().sum()
    for col_name in tbl.columns[1:]:
        yield (pl.col(col_name) / total_all) * 100
# पहली कॉलम और बनाए गए प्रतिशत कॉलम चुनने का प्रयास
xtab.select(cs.by_index(0), pct_exprs(xtab))
यह त्रुटि देता है:
TypeError: cannot create expression literal for value of type generator.
ध्यान देने की बात है कि केवल पहली कॉलम चुनना या केवल जनरेटेड अभिव्यक्तियाँ चुनना, दोनों अलग-अलग काम करते हैं; समस्या तब आती है जब इन्हें select में अलग-अलग आर्ग्युमेंट्स के रूप में साथ पास किया जाता है।
यह क्यों होता है
असल वजह यह है कि जब select को कई आर्ग्युमेंट्स मिलते हैं, और आप जनरेटर को सीधे पास कर देते हैं, तो वह एक अकेला आर्ग्युमेंट (टाइप: generator) के रूप में जाता है, न कि अभिव्यक्तियों के अनुक्रम के रूप में। API को या तो अलग-अलग एक्सप्रेशन चाहिए होते हैं या फिर किसी अनुक्रम को स्पष्ट रूप से अनपैक करके देना होता है। अनपैक किए बिना जनरेटर को एक लिटरल मान जैसा समझा जाता है, जिसे Polars सही तौर पर अस्वीकार कर देता है।
समाधान 1: जनरेटर को मैन्युअली अनपैक करें
जब आप कई आर्ग्युमेंट्स पास करते हैं, तो जनरेटर्स को मैन्युअली अनपैक करना जरूरी है। बात बस इतनी-सी है। स्टार ऑपरेटर का उपयोग करके जनरेटर को पोजिशनल आर्ग्युमेंट्स में फैला दें।
xtab.select(cs.by_index(0), *pct_exprs(xtab))
shape: (3, 3)
┌─────┬───────────┬───────────┐
│ a   ┆ 0         ┆ 1         │
│ --- ┆ ---       ┆ ---       │
│ i64 ┆ f64       ┆ f64       │
╞═════╪═══════════╪═══════════╡
│ 0   ┆ 33.333333 ┆ 33.333333 │
│ 1   ┆ 0.0       ┆ 16.666667 │
│ 2   ┆ 0.0       ┆ 16.666667 │
└─────┴───────────┴───────────┘
समाधान 2: ग्रैंड टोटल के लिए नैटिव Polars अभिव्यक्ति
यह काम Polars की एक्सप्रेशन-बिल्डिंग क्षमता से नैटिव तरीके से भी किया जा सकता है। नॉन-की कॉलम्स से sum और pl.sum_horizontal के साथ ग्रैंड टोटल निकालें, फिर चौड़े स्वरूप वाले कॉलम्स को उसी कुल से भाग दें। इससे सब कुछ लेज़ी-क्षम अभिव्यक्तियों के रूप में रहता है।
# पंक्तियों के पार प्रति-कॉलम कुल
xtab.select(cs.exclude(cs.first()).sum())
shape: (1, 2)
┌─────┬─────┐
│ 0   ┆ 1   │
│ --- ┆ --- │
│ u32 ┆ u32 │
╞═════╪═════╡
│ 2   ┆ 4   │
└─────┴─────┘
# सभी नॉन-की कॉलम का समग्र कुल
xtab.select(pl.sum_horizontal(cs.exclude(cs.first()).sum()))
shape: (1, 1)
┌─────┐
│ 0   │
│ --- │
│ u32 │
╞═════╡
│ 6   │
└─────┘
# प्रतिशत इनलाइन निकालें
xtab.with_columns(
    cs.exclude(cs.first()) / pl.sum_horizontal(cs.exclude(cs.first()).sum()) * 100
)
shape: (3, 3)
┌─────┬───────────┬───────────┐
│ a   ┆ 0         ┆ 1         │
│ --- ┆ ---       ┆ ---       │
│ i64 ┆ f64       ┆ f64       │
╞═════╪═══════════╪═══════════╡
│ 0   ┆ 33.333333 ┆ 33.333333 │
│ 1   ┆ 0.0       ┆ 16.666667 │
│ 2   ┆ 0.0       ┆ 16.666667 │
└─────┴───────────┴───────────┘
अगर आप इसे पुन: उपयोग के लिए ढाँचाबद्ध करना चाहें, तो सेलेक्टर्स और एक्सप्रेशंस को वैरिएबल्स में रखें और pipe के साथ लागू करें।
def to_percentages(wide_tbl):
    value_cols = cs.exclude(cs.first())
    grand = pl.sum_horizontal(value_cols.sum())
    return wide_tbl.with_columns(value_cols / grand * 100)
xtab.pipe(to_percentages)
यह क्यों मायने रखता है
एनालिटिक्स वर्कफ़्लो में क्रॉसटैब और पिवोट आम हैं। यह जानना कि कब किसी जनरेटर को अनपैक करना है, select की कम्पोज़िशन के दौरान उलझाऊ टाइप एरर्स से बचाता है। और पूरी गणना को नैटिव Polars अभिव्यक्तियों में रखना पाइपलाइन को सरल बनाता है—गणना के दौरान ही कुल और अनुपात निकाल पाएँगे, बिना एक्सप्रेशन API से बाहर गए।
मुख्य बातें
तुरंत समाधान के लिए, select में अन्य आर्ग्युमेंट्स के साथ जनरेटर आउटपुट मिलाते समय उसे अनपैक करें। अधिक इडियोमेटिक तरीके के लिए, sum और pl.sum_horizontal के साथ ग्रैंड टोटल बनाएँ और with_columns के जरिए प्रतिशत निकालें। दोनों पैटर्न कोड को संक्षिप्त, पूर्वानुमेय रखते हैं और Polars के सेलेक्टर्स व एक्सप्रेशंस के साथ सहजता से चलते हैं।
यह लेख StackOverflow पर प्रश्न (द्वारा robertspierre) और jqurious के उत्तर पर आधारित है।