2025, Sep 29 09:32
CSV में "" और missing को अलग रखें: pandas read_csv के भरोसेमंद तरीके
CSV में quoted "" और missing (,,) को अलग पहचानें: pandas read_csv के साथ खाली स्ट्रिंग बनाम NaN संभालने के व्यावहारिक तरीके, कोड उदाहरण, स्टेप-बाय-स्टेप गाइड.
जानबूझकर खाली स्ट्रिंग और CSV में अनुपस्थित मान को अलग-अलग पहचानना सुनने में आसान लगता है, पर pandas अक्सर दोनों को चुपचाप एक जैसा कर देता है। अगर आपकी फ़ाइल में "" जैसे खाली quoted फ़ील्ड और ,, जैसी सचमुच गायब प्रविष्टियाँ साथ मिल रही हों, तो सामान्यतः आप चाहेंगे कि पहला खाली स्ट्रिंग के रूप में बना रहे, जबकि दूसरा NaN में बदले। डिफॉल्ट रूप में read_csv() यह अंतर नहीं बचाता।
सेटअप: पढ़ते समय क्या खो जाता है
एक छोटा-सा CSV देखें, जहाँ दोनों पैटर्न साथ दिखते हैं:
id,name,age
1,,"25"
2,"",3
3,John,
डिफॉल्ट पार्सिंग के साथ लोड करने पर उद्धरणचिह्न मायने नहीं रखते; पार्सर संख्यात्मक प्रकारों का अनुमान लगा लेता है और खाली जगहों को NaN मान लेता है:
import io
import pandas as pd
sample = """id,name,age
1,,"25"
2,"",3
3,John,"""
frame_a = pd.read_csv(io.StringIO(sample))
print(frame_a.dtypes)
# id int64
# name object
# age float64
# dtype: object
ऐसा क्यों होता है
CSV में उद्धरणचिह्न केवल फ़ील्ड को सीमांकित करने और एस्केप करने का माध्यम हैं; वे किसी प्रकार (type) का संकेत नहीं होते। उद्धरण किसी फ़ील्ड के भीतर कॉमा या विशेष अक्षरों को रहने देते हैं, लेकिन प्रकार नहीं बदलते। इसलिए "25" आम तौर पर 25 की तरह ही पार्स होता है और संख्या समझा जाता है। इसी तरह, विभाजकों के बीच पूरी तरह खाली फ़ील्ड को missing के रूप में व्याख्यायित किया जाता है।
quotechar को किसी और प्रतीक में बदलना "" को सादा स्ट्रिंग के तौर पर बचा सकता है, लेकिन जैसे ही आपको सचमुच कॉमा वाले पाठ को सुरक्षित रखने के लिए डबल कोट्स चाहिए, यह तरीका टूट जाता है। साथ ही बाद में आपको हर संबंधित स्ट्रिंग से कोट्स साफ़ करने का झंझट भी उठाना पड़ता है।
व्यावहारिक तरीका: उद्धरणों की व्याख्या बंद करें, फिर सामान्यीकरण करें
यदि आपके quoted फ़ील्ड में कभी भी सेपरेटर नहीं आता, तो आप फ़ाइल को quoting=3 (csv.QUOTE_NONE) के साथ पढ़ सकते हैं ताकि कोट्स को साधारण अक्षर माना जाए, और फिर सिर्फ़ object कॉलमों में आगे/पीछे के कोट्स हटा दें। इससे भेद बना रहता है: ,, NaN बनता है, जबकि "" सफाई के बाद खाली स्ट्रिंग रह जाता है।
import io
import pandas as pd
payload = """id,name,age
1,,"25"
2,"",3
3,John,"""
grid = pd.read_csv(io.StringIO(payload), quoting=3)
# id name age
# 0 1 NaN "25"
# 1 2 "" 3
# 2 3 John NaN
grid.update(
grid.select_dtypes('O').apply(
lambda col: col.str.replace(r'^"|"$', '', regex=True)
)
)
print(grid)
# id name age
# 0 1 NaN 25
# 1 2 3
# 2 3 John NaN
इस बिंदु पर, age में जो भी मान NaN नहीं हैं, वे स्ट्रिंग हैं, भले ही देखने में संख्या लगें:
print(grid['age'].tolist())
# ['25', '3', nan]
विकल्प: संख्याएँ संख्या ही रहें, और quoted स्ट्रिंग्स स्ट्रिंग ही रहें
अगर आप चाहते हैं कि "25" स्ट्रिंग ही रहे, जबकि 3 संख्यात्मक बना रहे, तो पहले संख्यात्मक मानों को coercion के साथ बदलें और बाकी के लिए de-quoted टेक्स्ट पर वापस आ जाएँ:
import io
import pandas as pd
payload = """id,name,age
1,,"25"
2,"",3
3,John,"""
canvas = pd.read_csv(io.StringIO(payload), quoting=3)
canvas.update(
canvas.select_dtypes('O').apply(
lambda col: pd.to_numeric(col, errors='coerce').combine_first(
col.str.replace(r'^"|"$', '', regex=True)
)
)
)
print(canvas)
# id name age
# 0 1 NaN 25
# 1 2 3.0
# 2 3 John NaN
print(canvas['name'].tolist())
# [nan, '', 'John']
print(canvas['age'].tolist())
# ['25', 3.0, nan]
महत्वपूर्ण सीमा
quoting=3 वाला तरीका इस धारणा पर टिका है कि quoted फ़ील्ड में सेपरेटर नहीं आता। जैसे ही किसी quoted मान को कॉमा शामिल करने के लिए सचमुच quoting की ज़रूरत पड़ती है, यह तरीका काम नहीं करेगा:
id,name,age
1,,"25"
2,",",3
3,John,
ऐसी स्थितियों में, अगर आपको quoting के आधार पर प्रकार निकालने ही हैं, तो फ़ाइल को पहले से प्रोसेस करना या कस्टम पार्सर लिखना सबसे सुरक्षित रहता है।
यह फर्क मायने क्यों रखता है
अक्सर खाली स्ट्रिंग और missing मान के अर्थ अलग होते हैं। एनालिटिक्स, joins, validations और उपयोगकर्ताओं को जाने वाले एक्सपोर्ट — सब इस फर्क पर निर्भर कर सकते हैं। इन्गेस्ट पाइपलाइन की शुरुआत में ही इसे सुरक्षित रखना सूक्ष्म बग्स से बचाता है और बाद की रीवर्क से भी।
निष्कर्ष
CSV अपने आप में प्रकार की जानकारी एन्कोड नहीं करता; उद्धरण इसे नहीं बदलते। अगर आपको ,, को NaN और "" को खाली स्ट्रिंग की तरह बरतना है, तो quoting निष्क्रिय करके पढ़ें और फिर टेक्स्टुअल कॉलमों को सामान्य करें। अगर साथ ही आप संख्यात्मक फ़ील्ड को संख्या ही रखना चाहते हैं और quoted टेक्स्ट को स्ट्रिंग के रूप में, तो पहले numeric coercion लगाएँ और बाकी के लिए de-quoted टेक्स्ट अपनाएँ। जब आपका डेटा सच में डिलिमीटर्स को बचाने के लिए quotes पर निर्भर करता है, तो प्री-प्रोसेसिंग या कस्टम पार्सर की योजना बनाएँ।
यह लेख StackOverflow के एक प्रश्न (लेखक: Despicable me) और mozway के उत्तर पर आधारित है।