数据分析—NaN数据处理

目的

  1.查找NaN值(定位到哪一列、在列的哪个索引位置)

  2.填充NaN值(向上填充、向下填充、线性填充等)

  3.过滤NaN值

构建简单的Dataframe数据结构环境

import pandas as pd
import numpy as np

#在df中nan和None都会被自动填充为NaN
df=pd.DataFrame({'a':[np.nan,1,2,3],'b':[None,5,6,7],'c':[8,9,10,11]})
print(df)

'''结果 
     a    b   c
0  NaN  NaN   8
1  1.0  5.0   9
2  2.0  6.0  10
3  3.0  7.0  11

'''

注意点:

  1.None、nan在构建dataframe数据结构中都会被识别为NaN

  2.None与nan的类型是不一样的

#nan是个特殊的float类型
print(type(np.nan))  #<class 'float'>

#None是NoneType
print(type(None))   #<class 'NoneType'>

#打印空值看输出结果
print(df['a'][0])  #nan

#作为字典的key时,会被认为是不同的key
dic={None:1, NaN:2}
print(dic) #{None: 1, nan: 2}

目的1:查找NaN值

原始数据:

df=pd.DataFrame({'a':[0,1,np.nan,3,4],'b':[4,5,6,np.nan,8],'c':[9,10,np.nan,np.nan,13]})
print(df)

'''结果
     a    b     c
0  0.0  4.0   9.0
1  1.0  5.0  10.0
2  NaN  6.0   NaN
3  3.0  NaN   NaN
4  4.0  8.0  13.0

'''

1.1 输出NaN值所在具体位置(调用math下面的isnan做判断)

from math import isnan
for i in df.columns:
    # print(df[i].values) 
    for k in range(len( df[i].values)):
        if isnan(df[i].values[k]):
            print('字段%s存在NaN值:' % i + '索引位置是:%s'%k)

1.2 分别找到NaN值所在的列名、行名:np.isnan()

import pandas as pd
import numpy as np
from math import isnan

#在df中nan和None都会被自动填充为NaN
df=pd.DataFrame({'a':[np.nan,1,2,3,4],'b':[4,5,6,np.nan,np.nan],'c':[9,10,np.nan,np.nan,np.nan]},index=['index_0','index_1','index_2','index_3','index_4'])
print(df)
'''原始数据
           a    b     c
index_0  NaN  4.0   9.0
index_1  1.0  5.0  10.0
index_2  2.0  6.0   NaN
index_3  3.0  NaN   NaN
index_4  4.0  NaN   NaN
'''
#利用np.isnan(df),对整个df的NaN值做判断
res=np.isnan(df)
print(res)
'''
             a      b      c
index_0   True  False  False
index_1  False  False  False
index_2  False  False   True
index_3  False   True   True
index_4  False   True   True

'''

nan_res=np.where(np.isnan(df))
print(nan_res)  #(array([0, 2, 3, 3, 4, 4], dtype=int32), array([0, 2, 1, 2, 1, 2], dtype=int32))
'''
#得到一个元祖 
(array([0, 2, 3, 3, 4, 4], dtype=int32), array([0, 2, 1, 2, 1, 2], dtype=int32))
元祖第一个值:表示行索引(都是以数字表示)
元祖第二个值:表示列索引(都是以数字表示)
注意:看前后行、列索引列表的排序,发现是每一行的数据去逐一检索的
'''

#查看NaN值默认行号(数字)(有重复值时表示是这一行里有多个NaN值)
print(nan_res[0]) #[0 2 3 3 4 4]
print(nan_res[1]) #[0 2 1 2 1 2]

#查看NaN值的行号名(即实际行号名字)
nan_index_info=df.index[np.where(np.isnan(df))[0]]
print(nan_index_info) #Index(['index_0', 'index_2', 'index_3', 'index_3', 'index_4', 'index_4'], dtype='object')

#查看NaN值的列名
nan_columns_info=df.columns[np.where(np.isnan(df))[1]]
print(nan_columns_info) #Index(['a', 'c', 'b', 'c', 'b', 'c'], dtype='object')

1.3 isnull、notnull

isnull_res=df.isnull()
print(isnull_res)
'''
       a      b      c
0  False  False  False
1  False  False  False
2   True  False   True
3  False   True   True
4  False  False  False

'''

1.4 isnull().values

# 2.1 isnull().values
isnull_res=df.isnull().values
print(isnull_res)
'''
[[False False False]
 [False False False]
 [ True False  True]
 [False  True  True]
 [False False False]]
 
'''

1.5 isnull().sum() 统计显示

#2.3 isnull().sum() 统计
isnull_res=df.isnull().sum()
print(isnull_res)
'''
a    1
b    1
c    2
dtype: int64

'''

 1.6 isnull().any() 布尔值,显示行里是不是有NaN值

df=pd.DataFrame({'a':[0,1,np.nan,3,4],'b':[4,5,6,np.nan,8],'c':[9,10,np.nan,np.nan,13]})
print(df)
'''原始数据
     a    b     c
0  0.0  4.0   9.0
1  1.0  5.0  10.0
2  NaN  6.0   NaN
3  3.0  NaN   NaN
4  4.0  8.0  13.0

'''

any_res=df.isnull().any()
print(any_res)
'''
a    True
b    True
c    True
dtype: bool

'''

目的2:填充NaN值

原始数据样子:

df=pd.DataFrame({'a':[0,1,np.nan,3,4],'b':[4,5,6,np.nan,8],'c':[9,10,np.nan,np.nan,13]})
print(df)

'''结果
     a    b     c
0  0.0  4.0   9.0
1  1.0  5.0  10.0
2  NaN  6.0   NaN
3  3.0  NaN   NaN
4  4.0  8.0  13.0

'''

2.1 fillna直接填充

#2.1.fillna直接填充
df.fillna(0,inplace=True) #表示更新到源数据
print(df)
'''
     a    b     c
0  0.0  4.0   9.0
1  1.0  5.0  10.0
2  0.0  6.0   0.0
3  3.0  0.0   0.0
4  4.0  8.0  13.0

'''

#另外一种方式
res=df.where(df.notna(),100)

fillna({字典})字典key:表示列索引  value:表示要填充的值

df=pd.DataFrame({'a':[0,1,2,3,4],'b':[4,5,6,np.nan,8],'c':[9,10,np.nan,np.nan,13]})

#意为将b列空值:填充为00 c列空值填充为:11
df_new
=df.fillna({'b':00,'c':11}) print(df_new) ''' a b c 0 0 4.0 9.0 1 1 5.0 10.0 2 2 6.0 11.0 3 3 0.0 11.0 4 4 8.0 13.0 '''

2.2 向上填充(即取NaN后面一个数值作为填充):bfill

fillna_res=df.fillna(method='bfill')
print(fillna_res)
'''
     a    b     c
0  0.0  4.0   9.0
1  1.0  5.0  10.0
2  3.0  6.0  13.0
3  3.0  8.0  13.0
4  4.0  8.0  13.0

'''

2.3 向下填充(即取NaN前面一个数值作为填充):ffill

fillna_res=df.fillna(method='ffill')
print(fillna_res)
'''
     a    b     c
0  0.0  4.0   9.0
1  1.0  5.0  10.0
2  1.0  6.0  10.0
3  3.0  6.0  10.0
4  4.0  8.0  13.0

'''

2.4 用一个数据代替NaN:pad  (功能类似于向下填充ffill)

fillna_res=df.fillna(method='pad')
print(fillna_res)
'''
     a    b     c
0  0.0  4.0   9.0
1  1.0  5.0  10.0
2  1.0  6.0  10.0
3  3.0  6.0  10.0
4  4.0  8.0  13.0


'''

2.5 平均值替换:df.mean()

#平均值替换:df.mean()
fillna_res=df.fillna(df.mean())
print(fillna_res)
'''
     a     b          c
0  0.0  4.00   9.000000
1  1.0  5.00  10.000000
2  2.0  6.00  10.666667
3  3.0  5.75  10.666667
4  4.0  8.00  13.000000

'''

2.6 指定替换具体列的NaN值

#选择指定替换哪些列
choose_fillna=df.fillna(df.mean()['a':'b'])
print(choose_fillna)
'''
     a     b     c
0  0.0  4.00   9.0
1  1.0  5.00  10.0
2  2.0  6.00   NaN
3  3.0  5.75   NaN
4  4.0  8.00  13.0

'''

2.7 限制每列替换NaN的个数:limit=

limit_res=df.fillna(df.mean(),limit=1)
print(limit_res)
'''
     a     b          c
0  0.0  4.00   9.000000
1  1.0  5.00  10.000000
2  2.0  6.00  10.666667
3  3.0  5.75        NaN
4  4.0  8.00  13.000000

'''

2.8  线性插值 df.interpolate(),如果存在第一个值是NaN的情况,这个NaN值无法被替换,需要单独再判断

df=pd.DataFrame({'a':[2,1,2,3,4],'b':[4,5,6,np.nan,2],'c':[9,10,np.nan,np.nan,4]},index=['index_0','index_1','index_2','index_3','index_4'])
print(df)
'''
         a    b     c
index_0  2  4.0   9.0
index_1  1  5.0  10.0
index_2  2  6.0   NaN
index_3  3  NaN   NaN
index_4  4  2.0   4.0

'''
#实际上上前一个值和后一个值得平均数,因为interpolate()假设函数是直线形式
print(df.interpolate())
'''
         a    b     c
index_0  2  4.0   9.0
index_1  1  5.0  10.0
index_2  2  6.0   8.0
index_3  3  4.0   6.0
index_4  4  2.0   4.0

'''

特殊情况的填充:

  重点:假设每一列数据的第一或最后为空(再用上面的普通方法就填充时就容易忽略掉1和最后的值)

解决思路:

#解决的一个问题是开头和结果都是NaN的情况
1.判断如果开头是NaN取下面最近的数值填充
2.如果结尾是NaN取上面最近的数值填充
3.最后再对整体的中间NaN进行替换,就可以向上或向下取值了代码 

初始数据结构:

df=pd.DataFrame({'a':[np.nan,1,np.nan,3,4],'b':[None,5,np.nan,7,None],'c':[9,10,np.nan,12,13]})
print(df)
df=pd.DataFrame({'a':[np.nan,1,np.nan,3,4],'b':[None,5,np.nan,7,None],'c':[9,10,np.nan,12,13]})
print(df)
'''
     a    b     c
0  NaN  NaN   9.0
1  1.0  5.0  10.0
2  NaN  NaN   NaN
3  3.0  7.0  12.0
4  4.0  NaN  13.0

'''

代码实现3个步骤:

#循环没一个列的value值(判断)
for i in df.columns:
    n=1
    for k in range(len(df[i].values)):
        #如果第一个值是NaN
        if isnan(df[i].values[0]):
            #找下面最近的不是NaN的值做填充
            if not isnan(df[i].values[k]):
                df[i].values[0]=df[i].values[k]

            # print('说明第一个值是0,要替换成下面离他最近的一个数值')

        #如果最后一个值是NaN
        elif isnan(df[i].values[len(df[i].values)-1]):
            n+=1
            #再判断倒数第二个值看是否是NaN(依次往上找,找到不是NaN的值做替换)
            if not isnan(df[i].values[len(df[i].values)-n]):
                df[i].values[len(df[i].values) - 1]=df[i].values[len(df[i].values)-n]

            # print('说明最后一个值是0,要替换成上面离他最近的一个数值')
            
    #最终对整体没一列中存在的NaN数据进行向前取值
    df=df.fillna(method='pad')

print(df)

目的3:过滤NaN值

 初始数据

df=pd.DataFrame({'a':[0,1,2,3,4],'b':[4,5,6,np.nan,8],'c':[9,10,np.nan,np.nan,13]})
print(df)
'''
   a    b     c
0  0  4.0   9.0
1  1  5.0  10.0
2  2  6.0   NaN
3  3  NaN   NaN
4  4  8.0  13.0

'''

3.1 删除存在NaN的行或列:dropna()

#默认删除存在NaN的行:
# res=df.dropna()
# print(res)
'''
   a    b     c
0  0  4.0   9.0
1  1  5.0  10.0
4  4  8.0  13.0

'''

3.2 删除存在NaN的列:axis=1

res=df.dropna(axis=1) #删除存在NaN的列
print(res)
'''
   a
0  0
1  1
2  2
3  3
4  4

'''

3.3 删除全为NaN的how='all'

#删除全为NaN的行
all_res=df.dropna(how='all')
print(all_res)

3.4 删除全为NaN的列(axis=1,how='all')

#删除全为NaN的列(axis=1,how='all')
df=pd.DataFrame({'a':[0,1,2,np.nan,4],'b':[4,5,6,np.nan,8],'c':[np.nan,np.nan,np.nan,np.nan,np.nan]})
print(df)
'''
     a    b   c
0  0.0  4.0 NaN
1  1.0  5.0 NaN
2  2.0  6.0 NaN
3  NaN  NaN NaN
4  4.0  8.0 NaN
'''
all_loc_res=df.dropna(axis=1,how='all')
print(all_loc_res)
'''
     a    b
0  0.0  4.0
1  1.0  5.0
2  2.0  6.0
3  NaN  NaN
4  4.0  8.0

'''

3.5 dropna(thresh=2参数),过滤NaN时再做近一步的条件筛选

#在df中nan和None都会被自动填充为NaN
df=pd.DataFrame({'a':[np.nan,1,2,3,4],'b':[4,5,6,np.nan,np.nan],'c':[9,10,np.nan,np.nan,np.nan]},index=['index_0','index_1','index_2','index_3','index_4'])
print(df)
'''原始数据
           a    b     c
index_0  NaN  4.0   9.0
index_1  1.0  5.0  10.0
index_2  2.0  6.0   NaN
index_3  3.0  NaN   NaN
index_4  4.0  NaN   NaN

'''

#过滤取值,保留在行方向上至少有3个非空的项:dropna(thresh=2)
res=df.dropna(thresh=2)
print(res)
'''
           a    b     c
index_0  NaN  4.0   9.0
index_1  1.0  5.0  10.0
index_2  2.0  6.0   NaN

'''

#过滤取值,保留在列方向上至少有3个非空的项
res=df.dropna(thresh=3,axis=1)
print(res)
'''
           a    b
index_0  NaN  4.0
index_1  1.0  5.0
index_2  2.0  6.0
index_3  3.0  NaN
index_4  4.0  NaN

'''
原文地址:https://www.cnblogs.com/yangzhizong/p/10271179.html