2025, Oct 19 00:31

Pandas DataFrame में decimal.Decimal कॉलम स्वतः float64 में कास्ट करें

Pandas DataFrame में decimal.Decimal कॉलमों को बिना कॉलम नाम हार्डकोड किए डेटा-चालित तरीके से स्वतः float64 में बदलें; थ्रेशहोल्ड और astype मैपिंग के व्यावहारिक टिप्स पाएं.

वित्तीय या मापन डेटा के साथ काम करते समय decimal.Decimal अक्सर उपयोग में आता है। जैसे ही ऐसे मान Pandas DataFrame में पहुँचते हैं, संबंधित कॉलम प्रायः object प्रकार में बदल जाते हैं — और यदि आप तुरंत संख्यात्मक व्यवहार चाहते हैं, तो यह आदर्श नहीं है। लक्ष्य है कि कॉलम नाम हार्डकोड किए बिना, निर्माण के समय ही सभी Decimal-आधारित कॉलम स्वतः float64 में बदल जाएँ।

प्रारंभिक सेटअप

मान लें कि एक डिक्शनरी में decimal.Decimal मानों की सूची है। DataFrame बनाने के बाद, संबंधित कॉलम float64 के बजाय object dtype ले लेता है:

import pandas as pd
from decimal import Decimal
payload = {
    'Item': ['Apple', 'Banana', 'Orange'],
    'Price': [Decimal('1.25'), Decimal('0.75'), Decimal('2.00')],
    'Quantity': [10, 20, 15]
}
frame = pd.DataFrame(payload)
print(frame.dtypes)
# आउटपुट:
# Item        object
# Price       object
# Quantity     int64
# dtype: object

यहाँ तक कि pd.DataFrame.from_records(..., coerce_float=True) भी भीतर के Decimal मानों को नहीं बदलता। और भले ही किसी ज्ञात कॉलम पर .astype(float) काम कर जाता है, यह तब मदद नहीं करता जब कॉलम के नाम पहले से न पता हों।

Decimal अंततः object क्यों बनता है

जब किसी कॉलम में Decimal इंस्टेंस होते हैं, तो Pandas एक सामान्य object dtype का अनुमान लगाता है। यह मूल Python ऑब्जेक्ट्स को तो बचाए रखता है, लेकिन नैटिव संख्यात्मक व्यवहार नहीं देता। समाधान यह है कि किन कॉलमों में वास्तव में Decimal मान हैं, इसे पहचानें और उन्हें एक ही पास में float में बदलें।

एक भरोसेमंद रूपांतरण पैटर्न

व्यावहारिक तरीका यह है कि लक्ष्य कॉलमों को डेटा से ही पहचाना जाए, एक मैपिंग बनाई जाए और उसे .astype में पास किया जाए। अगर पहली पंक्ति प्रतिनिधिक मानी जा सकती है, तो उसी से Decimal-टाइप कॉलम पहचानकर कास्ट कर सकते हैं:

from decimal import Decimal
probe = frame.iloc[0].map(type).eq(Decimal)
cast_map = dict.fromkeys(frame.columns[probe], float)
# उदाहरण: {'Price': float}
converted = frame.astype(cast_map)

इस तरह तर्क डेटा-चालित रहता है और कॉलम नाम हार्डकोड नहीं करने पड़ते।

डिटेक्शन थ्रेशहोल्ड चुनना

यदि केवल पहली पंक्ति पर निर्भर रहना जोखिमभरा लगे, तो पूरे कॉलम पर एक थ्रेशहोल्ड तय करें। आपकी सहनशीलता के अनुसार, कॉलम को तब बदलें जब सारे मान Decimal हों, जब कम-से-कम एक Decimal हो, या जब 90% से अधिक मान Decimal हों:

# सभी मान Decimal हों तो कॉलम को बदलें
cast_map = dict.fromkeys(frame.columns[frame.map(type).eq(Decimal).all()], float)
# कम-से-कम एक मान Decimal हो तो बदलें
cast_map = dict.fromkeys(frame.columns[frame.map(type).eq(Decimal).any()], float)
# यदि 90% से अधिक मान Decimal हों तो बदलें
cast_map = dict.fromkeys(
    frame.columns[frame.map(type).eq(Decimal).mean().gt(0.9)],
    float
)
converted = frame.astype(cast_map)

रूपांतरण के बाद प्रभावित कॉलमों पर नैटिव संख्यात्मक व्यवहार मिल जाता है। परिणामी dtypes कुछ इस तरह दिख सकते हैं:

Item        string[python]
Price              float64
Quantity             Int64
dtype: object

यह क्यों महत्वपूर्ण है

DataFrame में सुसंगत dtype बनाए रखना पूर्वानुमेय संख्यात्मक ऑपरेशंस, एग्रीगेशन और अन्य लाइब्रेरी के साथ अंतर-संचालन के लिए जरूरी है। जिन कॉलमों में Decimal अधिक हैं, उन्हें object रहने देना सूक्ष्म दिक्कतें ला सकता है — प्रदर्शन में गिरावट से लेकर डाउनस्ट्रीम गणनाओं में अप्रत्याशित व्यवहार तक। निर्माण के समय डेटा-चालित कास्टिंग करने से बिना हार्डकोडेड कॉलम-लिस्ट संभाले, संगत संख्यात्मक dtypes सुनिश्चित होते हैं।

निष्कर्ष

जब इनपुट में decimal.Decimal हो, तो डेटा को तय करने दें कि किन कॉलमों को कास्ट करना है। जिन कॉलमों में Decimal मान हों, उन्हें पहचानें, float के लिए मैपिंग बनाएँ और .astype लागू करें। चाहे आप पहली पंक्ति पर भरोसा करें या पूरे कॉलम पर कोई थ्रेशहोल्ड लगाएँ, मूल दृष्टिकोण एक जैसा रहता है: हार्डकोडिंग से बचें, तर्क को घोषणात्मक रखें, और सुनिश्चित करें कि आपके संख्यात्मक कॉलम सचमुच संख्यात्मक की तरह व्यवहार करें।

यह लेख StackOverflow पर प्रश्न (लेखक: Gino) और mozway द्वारा दिए गए उत्तर पर आधारित है।