2025, Sep 27 19:31

pandas में CSV के मिश्रित प्रकार संभालने का भरोसेमंद तरीका

CSV में संख्यात्मक कॉलमों में स्ट्रिंग/खाली मान हों तो pandas कैसे संभाले? on_bad_lines क्यों नहीं चलता, और dtype के बजाय पहले स्ट्रिंग पढ़कर to_numeric से सुरक्षित कास्टिंग करें—उदाहरण सहित.

CSV फाइलें अक्सर प्रकारों को मिला देती हैं, खासकर जब मानव-इनपुट की वजह से किसी ऐसे कॉलम में स्ट्रिंग आ जाती है, जिसे पूर्णांक होना चाहिए। अगर आप इसे पकड़ने के लिए on_bad_lines पर निर्भर हैं, तो यह मददगार नहीं होगा। यह पैरामीटर तभी सक्रिय होता है जब पार्सर को संरचनात्मक दिक्कत मिले—जैसे डिलिमिटर की गड़बड़ी या कॉलमों की संख्या में असंगति—न कि तब, जब कोई मान integer में कास्ट होने में असफल हो। पाइपलाइन बिगाड़े बिना ऐसा डेटा लेने का साफ तरीका यहां है।

समस्या सेटअप

कल्पना कीजिए, एक CSV जिसमें संख्यात्मक कॉलमों में कभी-कभी स्ट्रिंग या खाली मान आ जाते हैं।

id,name,age
1,,"25"
2,"",30
jj,John,

dtype संकेतों के साथ साधारण पढ़ना ठीक लगता है, लेकिन मिश्रित प्रकारों को आपकी अपेक्षा के मुताबिक नहीं संभालेगा।

import pandas as pd
col_types_map = {'id': 'int64', 'name': 'str', 'age': 'int'}
frame_raw = pd.read_csv('a.csv', dtype=col_types_map, on_bad_lines='warn', engine='python')

यह क्यों टूटता है

on_bad_lines पैरामीटर उन पार्सिंग विसंगतियों से निपटता है, जैसे किसी पंक्ति में अपेक्षा से अधिक या कम फील्ड होना। जब कोई फील्ड मान लक्षित dtype से मेल नहीं खाता, तब यह दखल नहीं देता। टोकनाइजेशन सफल होने के बाद कास्टिंग होती है, इसलिए integer कॉलम में jj जैसा मान on_bad_lines से नहीं गुजरता। नतीजतन या तो dtype लागू करते समय अपवाद मिलता है, या फिर ऐसा व्यवहार जिसे आप नहीं चाहते थे।

समाधान: पहले स्ट्रिंग के रूप में पढ़ें, फिर स्पष्ट रूप से रूपांतरित करें

विश्वसनीय तरीका यह है कि पहले सब कुछ स्ट्रिंग के रूप में पढ़ें, फिर लक्षित कॉलमों को pandas.to_numeric से बदलें। errors पैरामीटर आपको असंगत मानों को कैसे हैंडल करना है, यह चुनने देता है। coerce इस्तेमाल करने पर गलत संख्याएँ NaN बन जाती हैं। raise इस्तेमाल करने पर अपवाद फेंका जाता है। ignore इस्तेमाल करने पर मूल स्ट्रिंग जस की तस रहती है। चूँकि ignore अप्रचलित हो चुका है, आप एक छोटे से रैपर के साथ वही व्यवहार लागू कर सकते हैं।

import pandas as pd
rows_all_str = pd.read_csv('a.csv', dtype=str, on_bad_lines='warn', engine='python')
rows_all_str['id'] = pd.to_numeric(rows_all_str['id'], errors='coerce')
rows_all_str['age'] = pd.to_numeric(rows_all_str['age'], errors='coerce')

यदि आप NaN बनाने के बजाय गैर-संख्यात्मक मानों को ज्यों-का-त्यों रखना चाहते हैं, तो try/except के साथ एक सुरक्षित कास्टर का उपयोग करें।

def cast_numeric_safely(x):
    try:
        return pd.to_numeric(x)
    except Exception:
        return x
mixed_frame = pd.read_csv('a.csv', dtype=str, on_bad_lines='warn', engine='python')
mixed_frame['id'] = mixed_frame['id'].apply(cast_numeric_safely)
mixed_frame['age'] = mixed_frame['age'].apply(cast_numeric_safely)

कन्वर्ज़न रणनीति बदलकर आप मनचाहा व्यवहार चुन सकते हैं। pandas.to_numeric के दस्तावेज़ में errors के विकल्प समझाए गए हैं: coerce मानों को NaN में बदलता है, raise (डिफॉल्ट) अपवाद फेंकता है, और ignore स्ट्रिंग को बनाए रखता है। देखें: https://pandas.pydata.org/docs/reference/api/pandas.to_numeric.html

नतीजे कैसे दिखेंगे

ignore वाली लॉजिक (बिल्ट-इन या कस्टम) के साथ, गैर-संख्यात्मक मान स्ट्रिंग ही रहते हैं:

   id  name   age
0   1   NaN  25.0
1   2   NaN  30.0
2  jj  John   NaN

errors='coerce' उपयोग करने पर गलत संख्याएँ NaN बन जाती हैं:

    id  name   age
0  1.0   NaN  25.0
1  2.0   NaN  30.0
2  NaN  John   NaN

errors='raise' उपयोग करने पर पार्सिंग पहले खराब मान पर ही रुक जाती है:

ValueError: Unable to parse string "jj" at position 0

यह क्यों मायने रखता है

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

मुख्य बातें

टाइप संबंधी मुद्दों के लिए on_bad_lines पर भरोसा न करें; यह केवल संरचनात्मक पार्स त्रुटियों, जैसे अप्रत्याशित फील्ड गिनती, को संबोधित करता है। संदिग्ध कॉलमों को पहले स्ट्रिंग के रूप में पढ़ें, फिर pandas.to_numeric और स्पष्ट errors नीति के साथ बदलें। यदि आपको गैर-संख्यात्मक मानों को बनाए रखना है, तो अप्रचलित व्यवहार पर निर्भर होने की बजाय एक छोटा सुरक्षित कास्टर लागू करें। यह तरीका मंशा को स्पष्ट करता है, त्रुटि-प्रबंधन सुधारता है, और डेटा फ्लो को साफ रखता है।

यह लेख StackOverflow के एक प्रश्न (लेखक: Despicable me) और Aadvik के उत्तर पर आधारित है।