处理缺失值

pandas对象的所有描述性统计信息默认情况下是排除缺失值的。当清洗数据用于分析时,对缺失数据本身进行分析以确定数据收集问题或数据丢失导致的数据偏差通常很重要。

有一些处理NA的方法可以了解一下:

判断值是否为空

1
df.head(2).isnull() # 缺失时为true

过滤缺失值dropna

在DataFrame中可以删除所有值均为NA的行 或 列,也可以删除带有NA的行:

1
2
3
df.head(2).dropna(how='all')  # 全部为空才删除
df.head(2).dropna(how='all', axis=1)
df.head(2).dropna()

假设你只想保留包含一定数量的观察值的行。你可以用thresh参数来表示,keep only the rows at least N non-NA values.保留可用值大于等于N个的行。

1
2
3
4
5
6
7
8
9
10
11
12
13
df = pd.DataFrame (np.random .randn(8,7))

df.iloc[0,:] = np.nan
df.iloc[1,:6] = np.nan
df.iloc[2,:5] = np.nan
df.iloc[3,:4] = np.nan
df.iloc[4,:3] = np.nan
df.iloc[5,:2] = np.nan
df.iloc[6,0] = np.nan

df.dropna(thresh=2)

# 只展示6行

补充缺失值fillna

你有时可能需要以多种方式补全“漏洞”,而不是过滤缺失值(也可能丢弃其他数据)。
大多数情况下,主要使用fillna方法来补全缺失值。

调用fillna时,可以这么操作:

1
2
3
4
5
6
7
8
9
10
11
# 使用一个常数来替代缺失值
df.fillna(0) # inplace=True

# 使用字典填充
df.fillna({1: 0.5, 2: 1}) # 第一、二列分别进行填充

# 重建索引的插值方式
df.fillna(method='ffill', limit=2) #limit表示前向或者后向填充时最大的填充范围

# 使用平均值或者中位数填充
df.fillna(data.mean())

数据转换

删除重复值duplicated:

DataFrame的duplicated方法返回的是一个布尔值Series,这个Series反映的是每一行是否存在重复(要整行都相同),drop_duplicates返回的是DataFrame,内容是duplicated返回数组中为False的部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
df = pd.DataFrame({'年龄': [34, 23, 34], '儿女': [2, 1, 2]})
df.duplicated()

'''
0 False
1 False
2 True
dtype: bool
'''

df.drop_duplicates()
'''
0 34 2
1 23 1
'''

也可以选择额外的列操作,或是选择保留最后一个观测到的值。

1
data.drop_duplicates(['k1', 'k2'], keep='last')

使用函数或映射进行数据转换map

Series的map方法接收一个函数或一个包含映射关系的字典型对象:

1
2
3
4
5
6
7
yinsghe = {
'被映射': '对应结果',
'被映射': '对应结果',
'被映射': '对应结果'..
}

df['列名'] = df.map(yingshe) # 这样就会多出一列的内容

替代值replace

1
2
3
# data.replace(被替换,替换结果) or data.replace(字典)
data.replace(number/list, number/list)
# number->number,list->number, list->list

离散化与分箱cut

连续值经常需要离散化,或者分离成”箱子“进行分析,使用pd.cut(待分,区间)。看到的输出描述了由pandas. cut计算出的箱。你可以将它当作一个表示箱名的字符串数组;它在内部包含一个categories(类别)数组,它指定了不同的类别名称以及codes属性中的数据标签:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
a = [12, 14, 13, 21, 46, 34, 37, 60, 66, 69]
bins = [10, 20, 30, 40, 50, 60, 70]
cur = pd.cut(a, bins) # 进行分箱
'''
[(10, 20], (10, 20], (10, 20], (20, 30], (40, 50], (30, 40], (30, 40], (50, 60], (60, 70], (60, 70]]
Categories (6, interval[int64]): [(10, 20] < (20, 30] < (30, 40] < (40, 50] < (50, 60] < (60, 70]]
'''

(1)cur.categories # 查看被分成的类型

(2)pd.value_counts(cur) # 查看每种类型的个数
'''
IntervalIndex([(10, 20], (20, 30], (30, 40], (40, 50], (50, 60], (60, 70]],
closed='right',
dtype='interval[int64]')

(10, 20] 3
(60, 70] 2
(30, 40] 2
(50, 60] 1
(40, 50] 1
(20, 30] 1
dtype: int64
'''

pd.value_counts(cats)是对pandas.cut的结果中的箱数量的计数。

与区间的数学符号一致,小括号表示边是开放的,中括号表示它是封闭的(包括边)。你可以通过传递right=False来改变哪一边是封闭的:

1
2
3
4
5
cur = pd.cut(a, bins, right=False)
'''
[[10, 20), [10, 20), [10, 20), [20, 30), [40, 50), [30, 40), [30, 40), [60, 70), [60, 70), [60, 70)]
Categories (6, interval[int64]): [[10, 20) < [20, 30) < [30, 40) < [40, 50) < [50, 60) < [60, 70)]
'''

label可以定义箱名,之前是区间,现在就可以变成xxx。

1
2
3
4
5
6
cur = pd.cut(a, bins, label=['我', '的', '妈', '呀'])

'''
[我, 我, 我, 我, 的, 的, 的, 妈, 呀, 呀]
Categories (4, object): [我 < 的 < 妈 < 呀]
'''

如果你传给cut整数个的箱来代替显式的箱边,pandas将根据数据中的最小值和最大值计算出等长的箱。pd.cut(data, 4)表示均等分成4份,每份的箱内区间是相同的。

qcut是一个与分箱密切相关的函数,它基于样本分位数进行分箱。取决于数据的分布,使用cut通常不会使每个箱具有相同数据量的数据点。由于qcut使用样本的分位数,你可以通过qcut获得等长的箱,箱内的数量都是相同的。

置换和随机抽样permutation

使用numpy.random.permutation对DataFrame中的Series或行进行置换(随机重排序)是非常方便的。

主要是根据你想要的轴长度产生一个表示新顺序的整形数组。

1
df.take(sampler)

sampler是一个经过permutation后的新数组,然后df会根据sampler重排行。

要选出一个不含有替代值的随机子集,可以使用Series和DataFrame的sample方法。要生成一个带有替代值的样本(允许有重复选择),将replace=True传入sample方法:

1
2
3
df.sample(n=3, replace=True)

# 选出来的结果和df的列数相同,行数由n决定,且每个值不会重复。

计算指标/虚拟变量

将分类变量转换为“虚拟”或“指标”矩阵是另一种用于统计建模或机器学习的转换操作。如果DataFrame中的一列有k个不同的值,则可以衍生一个k列的值为1和0的矩阵或DataFrame。

pandas有一个get_dummies函数用于实现该功能。

1
2
3
4
5
6
7
8
9
10
11
df = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'b'], 'data1': range(6)})
pd.get_dummies(df['key'])

'''
0 0 1 0
1 0 1 0
2 1 0 0
3 0 0 1
4 1 0 0
5 0 1 0
'''