ailiteracynepal 🇳🇵
पाठ आकार

अध्याय ३ · खण्ड II · 30 मिनेट

प्रकार, मिति, र नेपाली पाठ एन्कोडिङ सुधार्ने

गलत प्रकार, दोहोरो पात्रो, र बिग्रिएको एन्कोडिङले नेपाली डाटासँग काम गर्ने हरेक निर्मातालाई समाउँछन्। तीन छोटा औजारले तीनैलाई सुधार्छन् — मिति सही पार्स गर्ने, जानीजानी प्रकार रूपान्तरण गर्ने, र सही एन्कोडिङमा फाइल पढ्ने।

तीन वर्गका समस्याले झन्डै हरेक नेपाली निर्मातालाई पहिलो वर्षमा समाउँछ: स्तम्भ जसका मान सङ्ख्यात्मक देखिन्छन् तर वास्तवमा स्ट्रिङ हुन्, मिति स्तम्भ जसले विक्रम सम्बत् र AD मिसाउँछ, र CSV जसले देवनागरी हुनुपर्ने ठाउँमा ?????? देखाउँछ। कुनै पनि गाह्रो छैन। सबै पहिलो पटक अल्मलाउने हुन्। यो खण्डले तीनैलाई सँगै सिकाउँछ किनभने तिनी प्रायः उही फाइलमा एकैचोटि आउँछन्।

प्रकार निदान गर्ने

कुनै पनि अव्यवस्थित डाटासेटमा पहिलो चाल pandas लाई हरेक स्तम्भको प्रकार के सोच्छ सोध्ने हो:

import pandas as pd

df = pd.read_csv("data/messy_ministry_export.csv")
print(df.dtypes)

आउटपुट यस्तो देखिन सक्छ:

district     object
school_id    object
students     object
date         object
dtype: object

सबै object छन् — pandas को “स्ट्रिङ वा मिश्रित” नाम। यो तपाईंको पहिलो सङ्केत हो कि केही गलत छ: पूर्णाङ्क हुनुपर्ने students स्तम्भ स्ट्रिङका रूपमा बसेको छ।

दोस्रो हेराइ:

df["students"].head()
0      245
1     1,250
2      178
3       NA
4      820
Name: students, dtype: object

1,250 मा अल्पविरामले pandas लाई स्तम्भलाई स्वतः सङ्ख्यात्मक रूपमा पत्ता लगाउनबाट रोक्यो। र NA स्ट्रिङ हो, छुटेको मान होइन।

जानीजानी प्रकार रूपान्तरण गर्ने

स्ट्रिङ स्तम्भलाई सङ्ख्यात्मकमा रूपान्तरण गर्ने ढाँचा:

df["students"] = (
    df["students"]
    .str.replace(",", "", regex=False)
    .replace("NA", pd.NA)
    .astype("Int64")
)

एक शृङ्खलामा तीन चरण:

  • .str.replace(",", "", regex=False) ले अल्पविराम हटाउँछ। regex=False छिटो छ र अल्पविरामलाई regex ढाँचाका रूपमा व्यवहार गर्नबाट जोगाउँछ।
  • .replace("NA", pd.NA) ले "NA" स्ट्रिङलाई pandas को वास्तविक छुटेको-मान मार्करमा बदल्छ।
  • .astype("Int64") (ठूलो-I) ले nullable पूर्णाङ्क प्रकारमा रूपान्तरण गर्छ — सादा int को विपरीत, यसले छुटेका मान float मा बदलिनबिनै राख्न सक्छ।

यसपछि:

print(df["students"].head())
print(df["students"].sum())

तपाईंलाई वास्तविक पूर्णाङ्क मिल्छन्, छुटेकाका लागि pd.NA सहित, र अंकगणित चल्छ।

खराब मानमा विफल नहुने अझ अनुमतिशील रूपान्तरण:

df["students"] = pd.to_numeric(df["students"], errors="coerce")

errors="coerce" ले pandas ले पार्स गर्न नसक्ने जुनसुकैलाई NaN मा बदल्छ। स्तम्भमा कुन खराब मान लुकेका छन् थाहा नभएको बेला पहिलो-प्रहार रूपान्तरणका लागि उपयोगी।

मिति पार्स गर्ने — BS सहित

मिति दोस्रो विश्वव्यापी टाउको दुखाइ हो। सामान्य नेपाली मन्त्रालय CSV मा हुनसक्छ:

date
2026-06-23
2026/06/24
23-06-2026
2080-03-09

चौथो पङ्क्ति विक्रम सम्बत् मा छ। अरू AD मा, तीन फरक ढाँचामा।

पहिलो चरण सधैं: डाटा मालिकलाई सोध्नुहोस् कि प्रत्येक स्तम्भले कुन पात्रो प्रयोग गर्छ। यदि सोध्न सक्नुहुन्न भने, सन्दर्भ खोज्नुहोस् — वरिपरि आर्थिक-वर्षको उल्लेख, BS-विशिष्ट महिना नाम (श्रावण, भदौ), वा नमुना जहाँ वर्ष स्पष्ट रूपमा 2070 भन्दा माथि छ (झन्डै निश्चित रूपमा BS) वा 1990 देखि 2030 दशकमा (झन्डै निश्चित AD, यद्यपि 2080 दुवैमा सम्भव छ)।

पात्रो थाहा पाएपछि, पार्सिङ:

df["date_ad"] = pd.to_datetime(
    df["date"],
    errors="coerce",     # पार्स विफल हुने पङ्क्ति NaT (Not a Time) हुन्छन्
    format="mixed",      # pandas लाई प्रति पङ्क्ति धेरै ढाँचा कोसिस गर्न दिनुहोस्
    dayfirst=False,      # अस्पष्ट DD-MM-YYYY बनाम MM-DD-YYYY लाई पूर्वनिर्धारित रूपमा MM-DD ठान्नुहोस्
)

pd.to_datetime ले सामान्य AD ढाँचा सम्हाल्छ। मिश्रित dayfirst का लागि (नेपाली सन्दर्भ प्रायः DD-MM-YYYY हुन्छ), यदि निश्चित हुनुहुन्छ भने dayfirst=True सेट गर्नुहोस्।

BS मितिका लागि, तपाईंलाई रूपान्तरक पुस्तकालय चाहिन्छ। 2026 मा सबैभन्दा सफा nepali_datetime हो:

pip install nepali-datetime
from nepali_datetime import date as nepali_date

bs = nepali_date(2080, 3, 9)
ad = bs.to_datetime_date()
print(ad)   # datetime.date(2023, 6, 23)

पूरा स्तम्भ रूपान्तरण गर्न, सानो फङ्क्सन लेखेर लागू गर्नुहोस्:

from nepali_datetime import date as nepali_date

def bs_string_to_ad(s):
    try:
        y, m, d = map(int, s.split("-"))
        return nepali_date(y, m, d).to_datetime_date()
    except Exception:
        return None

df["date_ad"] = df["date_bs"].apply(bs_string_to_ad)

एन्कोडिङ समस्या, अन्ततः

तेस्रो विश्वव्यापी टाउको दुखाइ: तपाईंको CSV ले देवनागरी हुनुपर्ने ठाउँमा ?????? वा पà¥à¤à¤¤à¥à¤ª देखाउँछ।

यो एन्कोडिङ मेल नभएको हो। फाइल एक वर्ण एन्कोडिङमा लेखिएको थियो र अर्कोमा पढिँदै छ। उपचार सही एन्कोडिङमा पढ्ने हो।

# सामान्य अपराधी 1: Windows का लागि Excel ले CSV लाई 'cp1252' वा 'utf-8-sig' मा बचाउँछ
df = pd.read_csv("data/file.csv", encoding="utf-8-sig")

# सामान्य अपराधी 2: पुराना प्रणालीले Latin-1 प्रयोग गर्छन्
df = pd.read_csv("data/file.csv", encoding="latin-1")

# आधुनिक पूर्वनिर्धारित र 95% केसका लागि सही उत्तर
df = pd.read_csv("data/file.csv", encoding="utf-8")

कुन प्रयोग गर्ने पत्ता लगाउने तरिका:

  1. पहिले encoding="utf-8" कोसिस गर्नुहोस्। यदि UnicodeDecodeError फ्याँक्छ भने, फाइल UTF-8 होइन।
  2. encoding="utf-8-sig" कोसिस गर्नुहोस्। यो “BOM सहितको UTF-8” हो — Windows का लागि Excel ले प्रायः उत्पादन गर्ने। UTF-8 विफल हुने अधिकांश फाइल यहाँ सफल हुन्छन्।
  3. encoding="latin-1" कोसिस गर्नुहोस्। यो कहिल्यै त्रुटि दिँदैन तर गलत वर्ण उत्पादन गर्न सक्छ; 1 र 2 विफल हुँदा मात्र प्रयोग गर्नुहोस्।
  4. अन्तिम उपायका रूपमा, chardet स्थापना गर्नुहोस् र अनुमान गराउनुहोस्:
pip install chardet
import chardet

with open("data/file.csv", "rb") as f:
    raw = f.read(10000)
guess = chardet.detect(raw)
print(guess)   # {'encoding': 'utf-8-sig', 'confidence': 0.99, 'language': ''}

सही एन्कोडिङमा फाइल पढिएको बेला पनि, अघिल्लो प्रशोधनले पहिले नै पाठ बिगारेको हुनसक्छ। एक पटक देवनागरी ?????? का रूपमा फाइलमा लेखिएपछि, मूल वर्ण गएका छन्। त्यो बिन्दुदेखि कुनै उपचार छैन। एक मात्र बाटो स्रोतबाट पुन: निर्यात गर्ने हो।

सही एन्कोडिङमा फाइल लेख्ने

उल्टो समस्या: जब तपाईं डाउनस्ट्रिम औजार (Windows का लागि Excel, भनौँ) ले खोल्ने CSV लेख्नुहुन्छ, ती औजारले अपेक्षा गर्ने एन्कोडिङमा लेख्नुहोस्।

df.to_csv("data/out.csv", index=False, encoding="utf-8-sig")

utf-8-sig ले अग्रणी byte order mark सहित UTF-8 फाइल लेख्छ, जुन देवनागरीलाई Windows का लागि Excel मा सही देखाउने कुरा हो। सादा utf-8 अधिकांश अरू औजारका लागि चल्छ।

पूर्ण सफा गर्ने प्रहार — प्रकार, मिति, एन्कोडिङ

तीनै गर्ने एकल स्क्रिप्ट:

import pandas as pd
from nepali_datetime import date as nepali_date


def bs_string_to_ad(s):
    try:
        y, m, d = map(int, str(s).split("-"))
        return nepali_date(y, m, d).to_datetime_date()
    except Exception:
        return None


# 1. सही एन्कोडिङमा पढ्नुहोस्
df = pd.read_csv("data/messy_ministry_export.csv", encoding="utf-8-sig")

# 2. सङ्ख्यात्मक स्तम्भ सुधार्नुहोस्
df["students"] = (
    df["students"]
    .astype(str)
    .str.replace(",", "", regex=False)
    .replace({"NA": pd.NA, "": pd.NA})
    .astype("Int64")
)

# 3. BS मितिलाई AD मा पार्स गर्नुहोस्
df["date_ad"] = df["date_bs"].apply(bs_string_to_ad)
df["date_ad"] = pd.to_datetime(df["date_ad"])

# 4. डाउनस्ट्रिम-मित्रवत् एन्कोडिङमा बचाउनुहोस्
df.to_csv("data/clean/ministry.csv", index=False, encoding="utf-8-sig")

सोह्र लाइन सफा गर्ने कोड। यिनले, साना अनुकूलनसहित, तपाईंले कहिल्यै भेट्ने अधिकांश नेपाली सरकारी र NGO डाटासेटमा चल्नेछन्।

आफ्नो बुझाइ जाँच्नुहोस्

Quick check

तपाईंले Python मा `pd.read_csv('data/file.csv')` ले CSV खोल्नुहुन्छ र देवनागरी स्तम्भ `पà¥à¤à¤¤à¥à¤ª` का रूपमा देखिन्छ। तपाईं `encoding='utf-8'` कोसिस गर्नुहुन्छ र `UnicodeDecodeError` पाउनुहुन्छ। सम्भावित उपचार के हो?

Quick check

`students` भन्ने CSV स्तम्भमा `'1,250'`, `'NA'`, `'845'` जस्ता मान छन्। तपाईंलाई जोड्न मिल्ने सङ्ख्यात्मक स्तम्भ चाहिन्छ। सबैभन्दा बलियो दृष्टिकोण कुन हो?

अब के आउँछ

तपाईं अब कुनै पनि फाइल पढ्न, कुनै पनि स्तम्भ पार्स गर्न, र कुनै पनि मिति अनुवाद गर्न सक्नुहुन्छ। अन्तिम विश्वव्यापी सफा गर्ने काम मानहरू मानकीकरण गर्ने हो — Khalti, khalti, KHALTI, Khalti , र खल्ती सबैलाई उही "Khalti" मा बदल्ने। आकार उस्तै छ तर निर्णय सूक्ष्म छ, विशेष गरी नेपाली नाम र ठाउँका लागि। त्यो अर्को खण्ड हो।