Pandas数据清洗
数据清洗介绍
数据清洗实际上也是数据质量分析,检查原始数据中是否存在脏数据(不符合要求,或者不能直接进行分析的数据),并且处理脏数据。
常见情况如下
- 缺失值
- 异常值
- 重复数据
处理缺失值
Pandas使用浮点值NaN(not a Number)表示缺失值,并且缺失值在数据中时常出现。那么Pandas的目的之一就是“无痛地”处理缺失值。
判断数据是否为NaN
- pd.isnull(df) 返回哪些值是缺失值的布尔值
- pd.notnull(df) 返回值是isnull的反集
注意
- Python内建的None值也被当作NaN
过滤缺失值
- dropna(axis=0,how=’any’,inplace=False)
- axis 指定轴 默认为0 代表行
- how 默认为any 代表删除含有NaN的行 当为all 时代表删除所有值为NaN的行
- inplace 修改被调用的对象 而不是一个备份
# Series 过滤缺失值
ds = pd.Series([1, np.nan, 2, 3, 4, np.nan])
# 传统的过滤方法,布尔索引过滤
# ds[ds.notnull()]
ds.dropna() # dropna()过滤缺失值
# 输出结果, 含有NaN的那行就删掉了
0 1.0
2 2.0
3 3.0
4 4.0
dtype: float64
# DataFrame过滤缺失值
df = pd.DataFrame(np.arange(16).reshape(4,4))
df.iloc[1, :] = np.nan
df.iloc[3,1] = np.nan
# df.dropna() # 默认是删除含有NAN的行
df.dropna(axis=1) # 指定含有NAN的列
# 输出结果
0 1 2 3
0 0.0 1.0 2.0 3.0
2 8.0 9.0 10.0 11.0
补全缺失值(NaN)
- df.fillna(value=None,method=None,axis=None,inplace=False,limit=None)
- value 标量或字典对象用于填充缺失值
- method 插值方法 默认为”ffill”
- axis 需填充的轴 默认为0
- inplace 修改被调用的对象 而不是一个备份
- limit 用于向前或向后填充时最大的填充范围 ```python “”” Series填充,标量直接填充 “”” ds = pd.Series([1, np.nan, 2, 3, 4, np.nan]) ds.fillna(2)
输出结果
0 1.0 1 2.0 2 2.0 3 3.0 4 4.0 5 2.0 dtype: float64
<a name="KL1Hw"></a>
###
```python
"""
DataFrame数据填充
"""
df = pd.DataFrame(np.arange(12).reshape(3,4), index=list("ABC"), columns=list("WXYZ"))
df.loc["B",["X","Y"]] = np.nan
df.loc["C",["X","Y"]] = np.nan
# df.fillna(2) #默认情况下所有NAN填充相同标量值
# df.fillna({"X":3, "Y":4}) # value值可以为字典,这里的意思就是X列的NAN填充为3,Y列填充为4
df.fillna(method="ffill") # method参数为往上插补,默认是ffill; 功能类似于EXCEL单元格向下拉动
df.fillna(method="ffill", limit=1) # limit参数可以限制插补次数
df.fillna(df.mean()) #填充平均值或者中位数
异常值
脏数据也包含不符合要求的数据,那么对这块数据处理不能直接使用fillna填充。使用replace更加灵活。
- df.replace(to_replace=None,value=None)
- to_replace 去替换的值
- value 替换的值 ```python import numpy as np import pandas as pd
df = pd.DataFrame({“age”:[-1,20,19,27]})
df.replace(-1, df.median()) # 将-1替换为中值
df.replace([-1, 27], 23) # 一次性替换多个值
df.replace([-1, 27], [22, 23]) # 不同值替换不同的值
df.replace({-1:22, 27:23}) # 传入字典
> **注意: df.replace()是字符串方法, 如果要实现判断某个值的大小然后再替换,得要用df.where()**
- **df.where(cond, other=nan, inplace=False, axis=None, level=None, errors='raise', try_cast=False)**
- cond:判断条件,判断为真,值不变;判断为假,替换other的值
- other:需要替换的值,默认为NaA
- inplace 修改被调用的对象 而不是一个备份
- axis 需填充的轴 默认为0
<br />
```python
df.where((df<20, 23)
df.where((df>0) & (df<20), 23)
处理重复数据
判断重复值
- df.duplicated(subset=None, keep=’first’) 返回的一个布尔值Series 默认反映的是每一行是否与之前出现过的行相同
- subset 指定子列判断重复
- keep 默认为first保留首个出现的 last保留最后出现的
df2 = pd.DataFrame({
"age":[18,19,18,20],
"gender": ["male","female"] * 2,
"name": ["amy", "connor", "amy", "ben"]
})
"""
判断每一行是否有重复
"""
df2.duplicated() # 索引为2的与0重复, 输出布尔值
df2.duplicated(subset=["gender", "name"]) # 使用subset参数,根据gender和name两列判断重复
df2.duplicated(subset=["gender", "name"], keep="last") # keep参数默认是first,保留第一个;如果为last,则第一条为重复值,会标为True
删除重复值
- df.drop_duplicates() 返回的是DataFrame 默认删除重复行
- subset 指定的数据任何子集是否有重复
- keep 默认为first保留首个出现的 last保留最后出现的
# df2.drop_duplicates() # 删除重复行,保留第一行
# df2.drop_duplicates(subset=["gender","name"]) # 使用subset参数,根据gender和name两列判断重复值并删除行
# df2.drop_duplicates(subset=["gender","name"], keep="last") # 保留最后一个,删除前面的
离散化
离散化是把无限空间中有限的个体映射到有限的空间中去,以此提高算法的时空效率。
可以简单的理解为离散化就是将连续值进行分区间。
- pd.cut(x,bins) 将连续数据x进行离散化
- x 要进行离散化的数据
- bins 分组
- pd.value_counts(cates) 统计每个区间的数值分布 ```python import numpy as np import pandas as pd
age_arr = np.random.randint(20, 60, size=20)# 创建数组 age_arr
<a name="bvYJ1"></a>
###
```python
"""
以10岁为一个区间, 如20~30, 30~40, 40~50
"""
bins = [20,30,40,50,60] # 定义区间规则
cates = pd.cut(age_arr, bins) # 输出数组中每个值所在的区间
cates
#输出结果
[(30, 40], (40, 50], (50, 60], (50, 60], (40, 50], ..., (30, 40], (20, 30], (40, 50], (40, 50], (50, 60]]
Length: 20
Categories (4, interval[int64]): [(20, 30] < (30, 40] < (40, 50] < (50, 60]]
"""
codes 输出数据分组所在区间的组的索引
如果以区间分组,可以分为4组: 20~30, 30~40, 40~50,50~60,索引也是0、1、2、3 因此codes就是返回数据所在组的索引
"""
cates.codes # 返回数据分布所在组的索引
# 输出结果
array([1, 2, 3, 3, 2, 2, 1, 1, 2, 0, 3, 0, 1, 1, 0, 1, 0, 2, 2, 3],
dtype=int8)
"""
categories 输出分组的情况
closed='right',区间是右闭合
"""
cates.categories
# 输出结果
IntervalIndex([(20, 30], (30, 40], (40, 50], (50, 60]],
closed='right',
dtype='interval[int64]')
"""
自定义区间标签名称
"""
l_name = ["youth", "youngAdult", "MiddleAged", "Old"] # 定义标签名称
cates = pd.cut(age_arr, bins, labels=l_name)
cates
# 输出结果
['youngAdult', 'MiddleAged', 'Old', 'Old', 'MiddleAged', ..., 'youngAdult', 'youth', 'MiddleAged', 'MiddleAged', 'Old']
Length: 20
Categories (4, object): ['youth' < 'youngAdult' < 'MiddleAged' < 'Old']
"""
统计每个区间的数值分布
"""
pd.value_counts(cates)
#输出结果
MiddleAged 6
youngAdult 6
Old 4
youth 4
dtype: int64
计算虚拟变量
将分类变量转换为”虚拟”或”指标”矩阵是另一种用于统计建模或机器学习的转换操作。如果DataFrame中的一列有k个不同的值,则可以衍生一个K列的值为1和0的矩阵或DataFrame。
- pd.get_dummies() 将分类变量转换为”虚拟”或”指标”矩阵 ```python import numpy as np import pandas as pd
df = pd.DataFrame({ “key”:[“b”,”b”,”c”,”d”,”a”,”d”], “datal”:range(6) }) df
输出结果
key datal
0 b 0 1 b 1 2 c 2 3 d 3 4 a 4 5 d 5
```python
"""
将key列转换为虚拟矩阵
"""
pd.get_dummies(df["key"]) # 将key列传入
pd.get_dummies(df["key"], prefix="key") # prefix可以添加前缀
# 输出结果
key_a key_b key_c key_d
0 0 1 0 0
1 0 1 0 0
2 0 0 1 0
3 0 0 0 1
4 1 0 0 0
5 0 0 0 1
但是,如果说DataFrame中的一行属于多个类别,情况就会比较复杂。如下图
举例:
"""
读取IMDB-Movie-Data.csv对其分类(Genre)这列转化为虚拟矩阵
- 重新构建一个全为0的数组, 列:分类 行:原数组的行一致
- 如果某一条数据中的分类出现过, 赋值为1
"""
df = pd.read_csv("IMDB-Movie-Data.csv")
# df.head()
"""
1、构建分类列表
"""
all_genres = []
for x in df.Genre: # 获取某一列的值除了使用“[]”,也可以使用"."
# print(x)
l = x.split(",")
all_genres.extend(l) # 将所有值都放入一个列表中,然后等待去重
"""
去重方式有很多,例如:
- set()
- drop_duplicates
- unique
"""
genres = pd.unique(all_genres) # 使用unique去重
"""
2、构建全为0的数组
"""
zero_data = np.zeros((len(df), len(genres)) ) # 生成全为0的数组, 传入格式是元组, 输出array数组
zero_data = pd.DataFrame(zero_data, columns=genres) # array数组转为DataFrame数组
zero_data.head()
"""
3、如果某一条数据中的分类出现过, 赋值为1
- 选择数据(位置索引)
- 行索引: df.Genre定位到多少行, 行索引就是多少行
- 列索引: 获取到Genre里面每个元素所在的列索引的位置
"""
# 以Genre第一个数据为例
# df.columns.get_indexer()
f_data = df.Genre[0]
# print(f_data.split(",")) # 输出['Action', 'Adventure', 'Sci-Fi']
zero_data.columns.get_indexer(f_data.split(",")) # 获取到f_data每一个值的位置,也就是列索引的位置
for i,genre in enumerate(df.Genre): #遍历获取df.Genre中每一个元素以及对应的索引
col_index = zero_data.columns.get_indexer(genre.split(","))
# print(col_index)
# 通过位置索引选择数据并且赋值为1
zero_data.iloc[i,col_index] = 1 # zero_data.iloc[0, [0, 1, 2]]
zero_data.head()