最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python中處理缺失值的有效方法詳解

 更新時(shí)間:2025年06月18日 09:04:34   作者:菜鳥魔王  
在 Python 開發(fā)中,我們常常會(huì)遇到需要表示缺失值的場(chǎng)景,如何濫用None可能悄無(wú)聲息地埋下隱患,所以本文小編為大家整理了一些其他方法,希望對(duì)大家有所幫助

01 引言

在 Python 開發(fā)中,我們常常會(huì)遇到需要表示“缺失值”的場(chǎng)景。無(wú)論是處理 API 返回的數(shù)據(jù)、解析用戶輸入,還是管理緩存狀態(tài),開發(fā)者們的第一反應(yīng)往往是使用 None。然而,隨著代碼規(guī)模的增長(zhǎng)和業(yè)務(wù)邏輯的復(fù)雜化,None 的濫用卻可能悄無(wú)聲息地埋下隱患。

比如在這樣一個(gè)場(chǎng)景:你正在編寫一個(gè)用戶信息處理函數(shù),當(dāng)用戶未提供郵箱時(shí),你希望回退到一個(gè)默認(rèn)地址。于是,你寫下了這樣的代碼:

def send_notification(to=None):
    if to is None:
        to = "default@example.com"
    # 發(fā)送郵件...

表面上,這段代碼邏輯清晰,但問(wèn)題在于,None 在這里承載了多重含義:它可能表示用戶確實(shí)沒有提供郵箱,也可能是某個(gè)中間步驟尚未初始化數(shù)據(jù),甚至可能是開發(fā)者故意傳遞的合法值。這種語(yǔ)義上的模糊性,會(huì)在后續(xù)維護(hù)中逐漸顯現(xiàn)出它的破壞力——比如,當(dāng)另一個(gè)開發(fā)者試圖區(qū)分“未設(shè)置郵箱”和“主動(dòng)取消訂閱”時(shí),None 根本無(wú)法提供足夠的信息。

更糟糕的是,None 與 Python 中其他“假值”(如空字符串""、數(shù)字 0、布爾值 False)的行為高度相似。一個(gè)本應(yīng)檢查數(shù)據(jù)是否存在的條件判斷,可能因?yàn)槟硞€(gè)意外傳入的 0 而錯(cuò)誤地執(zhí)行了回退邏輯。這類問(wèn)題在調(diào)試時(shí)尤其棘手,因?yàn)槿罩局兄粫?huì)顯示一個(gè)孤零零的 None,而無(wú)法告訴你它究竟代表什么。

這就是為什么我們需要更好的工具。與其依賴None這一模糊的占位符,Python 開發(fā)者可以通過(guò)**自定義哨兵對(duì)象(Sentinel Objects)**來(lái)明確表達(dá)意圖。哨兵對(duì)象是獨(dú)一無(wú)二的實(shí)例,專門用于標(biāo)記“缺失”或“未初始化”狀態(tài),既避免了與合法值的沖突,又能讓代碼邏輯一目了然。接下來(lái)的內(nèi)容,我們將深入探討如何用哨兵對(duì)象重構(gòu)代碼,從而讓缺失值的處理變得更安全、更可維護(hù)。

02 None 的局限性

2.1 一個(gè)值,多重含義

None 最常見的濫用場(chǎng)景之一,就是被迫承擔(dān)多種不同的含義。例如,在初始化一個(gè)對(duì)象屬性時(shí),開發(fā)者可能會(huì)這樣寫:

class UserProfile:
    def __init__(self):
        self.cache = None  # 表示"稍后填充"

這里的 None 僅僅表示“緩存尚未加載”,但同一段代碼的其他部分可能會(huì)誤以為 None 代表“緩存已被清空”或“緩存不可用”。這種模糊性使得代碼的可讀性下降,尤其是在團(tuán)隊(duì)協(xié)作時(shí),不同的開發(fā)者可能會(huì)對(duì) None 的含義做出不同的假設(shè)。

更復(fù)雜的情況出現(xiàn)在 API 或數(shù)據(jù)處理中。假設(shè)我們有一個(gè)函數(shù),負(fù)責(zé)解析用戶的訂閱狀態(tài):

def get_subscription_status(user):
    status = user.get("subscription", None)
    if status is None:
        return "inactive"  # 是用戶未訂閱,還是數(shù)據(jù)缺失?

此時(shí),None 可能代表兩種完全不同的情況:

  • 數(shù)據(jù)缺失(用戶記錄中沒有 subscription 字段);
  • 顯式取消(用戶主動(dòng)退訂,字段被設(shè)為 None)。 如果業(yè)務(wù)邏輯要求區(qū)分這兩種情況,None 顯然無(wú)法勝任。

2.2 與 Python 假值的沖突

None 的另一個(gè)問(wèn)題在于,它和 Python 中的其他“假值”(falsy values)行為相似,容易導(dǎo)致意外的邏輯錯(cuò)誤。例如:

def process_value(value):
    if not value:  # 不僅檢查None,還會(huì)過(guò)濾0、""、False等
        value = default_value

2.3 難以追溯的空值來(lái)源

當(dāng)系統(tǒng)出現(xiàn)問(wèn)題時(shí),日志中的 None 往往無(wú)法提供足夠的上下文。例如,在數(shù)據(jù)處理流水線中,某個(gè)字段突然變成 None,開發(fā)者需要排查:

  • 是上游數(shù)據(jù)源遺漏了這個(gè)字段?
  • 是某個(gè)中間步驟顯式清空了它?
  • 還是代碼邏輯錯(cuò)誤地覆蓋了原有值?

如果使用自定義哨兵,就能在日志中清晰區(qū)分不同的空值狀態(tài),大幅縮短調(diào)試時(shí)間。

03 哨兵對(duì)象解決方案

在認(rèn)識(shí)到 None 的種種局限性后,我們需要一種更精確、更安全的替代方案。哨兵對(duì)象(Sentinel Objects)正是為此而生,它通過(guò)創(chuàng)建一個(gè)獨(dú)特的、不可混淆的對(duì)象實(shí)例,為缺失值提供了明確的語(yǔ)義表達(dá)。

哨兵對(duì)象最簡(jiǎn)單的實(shí)現(xiàn)方式就是創(chuàng)建一個(gè)普通的 object 實(shí)例:

MISSING = object()

由于 Python 中每個(gè) object()都會(huì)生成一個(gè)全新的唯一標(biāo)識(shí),MISSING 對(duì)象不會(huì)與任何其他值產(chǎn)生沖突。在實(shí)際使用時(shí),我們可以清晰地表達(dá)意圖:

def get_config_value(key):
    value = config.get(key, MISSING)
    if value is MISSING:
        raise ConfigError(f"Missing required config: {key}")

這種方式的優(yōu)勢(shì)顯而易見:首先,它完全避免了與 None、False、0 等其他“假值”的混淆;其次,代碼的意圖變得極其明確 - 我們不是在檢查某個(gè)值是否為 None,而是在確認(rèn)這個(gè)配置項(xiàng)是否真的存在。

為了使哨兵對(duì)象在調(diào)試和日志記錄時(shí)更加友好,我們可以進(jìn)一步優(yōu)化其實(shí)現(xiàn):

class _Missing:
    def __repr__(self):
        return "<MISSING>"

MISSING = _Missing()

這個(gè)增強(qiáng)版本在打印或記錄日志時(shí),會(huì)顯示有意義的<MISSING>標(biāo)識(shí),而不是默認(rèn)的 object 表示形式。

我們還可以創(chuàng)建哨兵家族:

class Sentinel:
    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return f"<{self.name}>"

MISSING = Sentinel("MISSING")
UNSET = Sentinel("UNSET")
DELETED = Sentinel("DELETED")

Python 內(nèi)置的 Ellipsis 對(duì)象(...)也可以作為輕量級(jí)的哨兵值使用:

def process_data(data=...):
    if data is ...:
        data = load_default_data()

Ellipsis 作為哨兵有其獨(dú)特優(yōu)勢(shì):它是 Python 內(nèi)置的單例對(duì)象,內(nèi)存占用極?。辉陬愋吞崾局幸灿刑囟ㄓ猛?,因此對(duì)類型檢查器友好。不過(guò)需要注意的是,過(guò)度使用 Ellipsis 可能會(huì)降低代碼可讀性,建議在團(tuán)隊(duì)內(nèi)部達(dá)成明確的使用約定。

04 方法補(bǔ)充

python實(shí)現(xiàn)數(shù)據(jù)集缺失值處理

1. 直接刪除

當(dāng)缺失值的個(gè)數(shù)只占整體很小一部分的時(shí)候,可直接刪除缺失值。但是如果缺失值占比上升,這種缺失值處理方法誤差就很大了。在采用刪除法處理缺失值時(shí),需要首先檢測(cè)樣本總體中缺失值的個(gè)數(shù)。python中統(tǒng)計(jì)缺失值的方法如下:

import numpy as np
import pandas as pd
data = pd.read_csv('data.csv',encoding='GBK')
# 將空值形式的缺失值轉(zhuǎn)換成可識(shí)別的類型
data = data.replace(' ', np.NaN)
print(data.columns)#['id', 'label', 'a', 'b', 'c', 'd']
#將每列中缺失值的個(gè)數(shù)統(tǒng)計(jì)出來(lái)
null_all = data.isnull().sum()
#id       0
#label    0
#a        7
#b        3
#c        3
#d        8
#查看a列有缺失值的數(shù)據(jù)
a_null = data[pd.isnull(data['a'])]
#a列缺失占比
a_ratio = len(data[pd.isnull(data['a'])])/len(data) #0.0007
#丟棄缺失值,將存在缺失值的行丟失
new_drop = data.dropna(axis=0)
print(new_drop.shape)#(9981,6)

#丟棄某幾列有缺失值的行
new_drop2 = data.dropna(axis=0, subset=['a','b'])
print(new_drop2.shape)#(9990,6)

上述數(shù)據(jù)缺失值較少,可直接刪除。注意,在計(jì)算缺失值時(shí),對(duì)于缺失值不是NaN的要用replace()函數(shù)替換成NaN格式,否則pd.isnull()檢測(cè)不出來(lái)。

2.使用一個(gè)全局常量填充缺失值

可以用一個(gè)常數(shù)('Unknow’或者負(fù)無(wú)限大)來(lái)填充缺失值。但是如果缺失值較多,都用’Unknow’來(lái)填充的話,數(shù)據(jù)挖掘程序會(huì)覺得’Unknow’是一個(gè)有趣的概念。該方法很簡(jiǎn)單,但十分不可靠。python實(shí)現(xiàn)如下:

       #用0填充缺失值
fill_data = data.fillna('Unknow')
print(fill_data.isnull().sum())
#out 
id       0
label    0
a        0
b        0
c        0
d        0

3.均值、眾數(shù)、中位數(shù)填充

根據(jù)樣本之間的相似性填補(bǔ)缺失值是指用這些缺失值最可能的值來(lái)填補(bǔ)它們,通常使用能代表變量中心趨勢(shì)的值進(jìn)行填補(bǔ),代表變量中心趨勢(shì)的指標(biāo)包括平均值、中位數(shù)、眾數(shù)等,那么我們采用哪些指標(biāo)來(lái)填補(bǔ)缺失值呢?

分布類型填充值原因
近正態(tài)分布平均值所有觀測(cè)值都較好地聚集在平均值周圍
偏態(tài)分布中位數(shù)偏態(tài)分布的大部分值都聚集在變量分布的一側(cè),中位數(shù)是更好地代表數(shù)據(jù)中心趨勢(shì)的指標(biāo)
有離群點(diǎn)的分布中位數(shù)中位數(shù)是更好地代表數(shù)據(jù)中心趨勢(shì)的指標(biāo)
名義變量眾數(shù)名義變量無(wú)大小、順序之分,不能加減乘除。如性別

python實(shí)現(xiàn)如下:

#均值填充
data['a'] = data['a'].fillna(data['a'].mean())
#中位數(shù)填充
data['a'] = data['a'].fillna(data['a'].median())
#眾數(shù)填充
data['a'] = data['a'].fillna(stats.mode(data['a'])[0][0])
#用前一個(gè)數(shù)據(jù)進(jìn)行填充
data['a'] = data['a'].fillna(method='pad')
#用后一個(gè)數(shù)據(jù)進(jìn)行填充
data['a'] = data['a'].fillna(method='bfill')

Imputer提供了缺失數(shù)值處理的基本策略,比如使用缺失數(shù)值所在行或列的均值、中位數(shù)、眾數(shù)來(lái)替代缺失值。

  from sklearn.preprocessing import Imputer
imr = Imputer(missing_values='NaN', strategy='mean', axis=0)
imr = imr.fit(data.values)
imputed_data = pd.DataFrame(imr.transform(data.values))
print(imputed_data[0:15])
參數(shù)描述
missing_valuesint或’NaN’,默認(rèn)NaN(String類型)
strategymean,默認(rèn)平均值填補(bǔ);可選,median(中位數(shù)),most_frequent(眾數(shù))
axis指定軸向。axis=0,列向(默認(rèn));axis=1,行向
verboseint默認(rèn)值為0
copy默認(rèn)True:創(chuàng)建數(shù)據(jù)集的副本;False:在任何地方都可進(jìn)行插值

4. 插值法、KNN填充

插值法

interpolate()插值法,計(jì)算的是缺失值前一個(gè)值和后一個(gè)值的平均數(shù)。

data['a'] = data['a'].interpolate()

KNN填充

from fancyimpute import KNN
fill_knn = KNN(k=3).fit_transform(data)
data = pd.DataFrame(fill_knn)
print(data.head())
#out 
       0    1    2       3         4    5
0  111.0  0.0  2.0   360.0  4.000000  1.0
1  112.0  1.0  9.0  1080.0  3.000000  1.0
2  113.0  1.0  9.0  1080.0  2.000000  1.0
3  114.0  0.0  1.0   360.0 *3.862873 *1.0
4  115.0  0.0  1.0   270.0  5.000000  1.0

5.隨機(jī)森林填充

from sklearn.ensemble import RandomForestRegressor
#提取已有的數(shù)據(jù)特征
process_df = data.ix[:, [1, 2, 3, 4, 5]]
# 分成已知該特征和未知該特征兩部分
known = process_df[process_df.c.notnull()].as_matrix()
uknown = process_df[process_df.c.isnull()].as_matrix()
# X為特征屬性值
X = known[:, 1:3]
# print(X[0:10])
# Y為結(jié)果標(biāo)簽
y = known[:, 0]
print(y)
# 訓(xùn)練模型
rf = RandomForestRegressor(random_state=0, n_estimators=200, max_depth=3, n_jobs=-1)
rf.fit(X, y)
# 預(yù)測(cè)缺失值
predicted = rf.predict(uknown[:, 1:3])
print(predicted)
#將預(yù)測(cè)值填補(bǔ)原缺失值
data.loc[(data.c.isnull()), 'c'] = predicted
print(data[0:10])

到此這篇關(guān)于Python中處理缺失值的有效方法詳解的文章就介紹到這了,更多相關(guān)Python處理缺失值內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • python異常處理并調(diào)試

    python異常處理并調(diào)試

    這篇文章主要介紹了python異常處理并調(diào)試,異常是錯(cuò)誤出現(xiàn)時(shí),可以在正常的控制流程之外采取的行為下面我們就來(lái)看看python的那些異常,需要的小伙伴可以參考一下
    2022-02-02
  • Python中文件的寫入讀取以及附加文字方法

    Python中文件的寫入讀取以及附加文字方法

    今天小編就為大家分享一篇Python中文件的寫入讀取以及附加文字方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-01-01
  • python占位符輸入方式實(shí)例

    python占位符輸入方式實(shí)例

    在本篇文章中小編給大家分享了關(guān)于python占位符怎么輸入的相關(guān)知識(shí)點(diǎn)內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。
    2019-05-05
  • python的range和linspace使用詳解

    python的range和linspace使用詳解

    今天小編就為大家分享一篇python的range和linspace使用詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-11-11
  • 顯卡驅(qū)動(dòng)CUDA?和?pytorch?CUDA?之間的區(qū)別

    顯卡驅(qū)動(dòng)CUDA?和?pytorch?CUDA?之間的區(qū)別

    本文主要介紹了顯卡驅(qū)動(dòng)CUDA?和?pytorch?CUDA?之間的區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • python讀取xml文件方法解析

    python讀取xml文件方法解析

    這篇文章主要介紹了python讀取xml文件方法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • Python實(shí)現(xiàn)字符串反轉(zhuǎn)的9種方法(最全)

    Python實(shí)現(xiàn)字符串反轉(zhuǎn)的9種方法(最全)

    本文主要介紹了Python實(shí)現(xiàn)字符串反轉(zhuǎn)的9種方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • Jupyter notebook 遠(yuǎn)程配置及SSL加密教程

    Jupyter notebook 遠(yuǎn)程配置及SSL加密教程

    這篇文章主要介紹了Jupyter notebook 遠(yuǎn)程配置及SSL加密教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-04-04
  • Python獲取網(wǎng)頁(yè)上圖片下載地址的方法

    Python獲取網(wǎng)頁(yè)上圖片下載地址的方法

    這篇文章主要介紹了Python獲取網(wǎng)頁(yè)上圖片下載地址的方法,涉及Python操作正則表達(dá)式匹配字符串的技巧,需要的朋友可以參考下
    2015-03-03

最新評(píng)論

三门县| 凤山县| 砀山县| 昭苏县| 灵川县| 灵丘县| 泾源县| 长武县| 沙田区| 松阳县| 黄平县| 永德县| 绥中县| 昌平区| 科技| 奉新县| 芦溪县| 汕尾市| 连平县| 天峻县| 北碚区| 北川| 昔阳县| 白河县| 石家庄市| 孝义市| 比如县| 兴山县| 伊吾县| 高唐县| 涟水县| 特克斯县| 冷水江市| 香港 | 宜黄县| 江津市| 玉环县| 财经| 中方县| 安宁市| 牡丹江市|