1 数据清洗
#### 概述
数据清洗是指对原始数据进行处理和转换,以去除无效、重复、缺失或错误的数据,使数据符合分析的要求。
#### 作用和意义
- 提高数据质量:
- 通过数据清洗,数据质量得到提升,减少错误分析和错误决策。
- 增加数据可用性:
- 清洗后的数据更加规整和易于使用,提高数据的可用性和可读性。
## 清洗维度
- 缺失值处理:
- 对于缺失的数据,可以删除包含缺失值的行或列或者填充缺失值。
- 重复值处理:
- 识别和删除重复的数据行,避免重复数据对分析结果产生误导。
- 异常值处理:
- 检测和处理异常值,决定是删除、替换或保留异常值。
## 缺失值清洗
##### 缺失值/空值的删除
- 伪造缺失值数据
import pandas as pd
from pandas import DataFrame, Series
df = pd.read_csv('./data/none.csv', index_col=0)
df # NaN就是None空白
- 缺失值的检测和删除,相关方法:
- isnull():检测df中的每一个元素是否为空值,为空则给该元素返回True,否则返回False
- notnull():检测df中的每一个元素是否为非空值,为非空则给该元素返回True,否则返回False
- any():检测一行或一列布尔值中是否存在一个或多个True,有则返回True,否则返回False
- all():检测一行或一列布尔值中是否存全部为True,有则返回True,否则返回False
- dropna():将存在缺失值/空值的行或者列进行删除
# 检测哪些列中存在控制
df.isnull()
# 检测非空值
df.notnull()
# 可以判断哪些列存在控制
# axis=0表示针对列进行any操作
# axis=1表示针对行进行any操作
df.isnull().any(axis=0)
df.notnull().all(axis=0)
- dropna()进行空值检测和过滤
# 直接返回删除空值对应后的结果,不会直接改变原始数据
df.dropna()
- 计算df中每一列存在缺失值的个数和占比
for col in df.columns:
# 满足该条件则表示第col列中存在空值
if df[col].isnull().sum() > 0:
# 求出该列空值的个数
null_count = df[col].isnull().sum()
# 求出该列中空值的占比: 空值的数量/列的总元素个数
p = format(null_count / df[col].size, '.2%')
print(col, null_count, p)
- 使用任意值填充空值
# 如果想应用于原始数据,就加上inplace=True
df.fillna(value=666)
- 使用近邻值填充空值
# 在竖直方向上,会用空值前面的值填充空值 ffill前值填充
df.fillna(axis=0, method='ffill').fillna(axis=0, method='bfill') # bfill后值填充
- 使用相关的统计值填充空值
# 可以空值列的均值、中位数、方差等统计指标对空值进行填充
for col in df.columns:
if df[col].isnull().sum() > 0:
# 计算出空值对应的均值
mean_value = df[col].mean()
df[col].fillna(value=mean_value, inplace=True)
df
注意:实现空值的清洗最好选择删除的方式,如果删除的成本比较高,再选择填充的方式。
2 重复值清洗
- 伪造重复行的数据源
df = pd.read_csv('data/repeat.csv', index_col=0)
df
- 使用duplicated()方法检测重复的行数据
df.duplicated().sum() # 2
- 使用drop_duplicates()方法检测且删除重复的行数据
df.drop_duplicates()
3 异常值清洗
异常值是分析师和数据科学家常用的术语,因为它需要密切注意,否则可能导致错误的估计。
简单来说,异常值是一个观察值,远远超出了样本中的整体模式。
异常值在统计学上的全称是疑似异常值,也称作离群点,异常值的分析也称作离群点分析。
异常值是指样本中出现的“极端值”,数据值看起来异常大或异常小,其分布明显偏离其余的观测值。
异常值分析是检验数据中是否存在不合常理的数据。
- 给定条件的异常数据处理
- 自定义一个1000行3列(A,B,C)取值范围为0-1的数据源,然后将C列中的值大于其两倍标准差的异常值进行清洗
data = pd.read_csv('./data/outlier.csv', index_col=0)
data
# C列的2倍标准差
twice_std = data['C'].std() * 2
twice_std
# 判定异常值, 即值大于2倍的标准差
ex = data['C'] > twice_std
ex # True表示异常值,False标志正常值
# 取出True对应的行数据 (异常值对应的行数据)
data.loc[ex]
# 提取异常值对应行数据的行索引
drop_index = data.loc[ex].index
drop_index
# 将异常值对应的行从数据表格中进行删除
data.drop(index=drop_index)
3 map映射
- 给Series中的一组数据提供另外一种表现形式,或者说给其绑定一组指定的标签或字符串。
- 创建一个df,两列分别是姓名和薪资,然后给其名字起对应的英文名,然后将英文的性别统一转换为中文的性别
dic = {
'name': ['tom', 'jerry', 'alex', 'tom'],
'salary': [10000, 20000, 15000, 21000],
'gender': ['male', 'female', 'male', 'female']
}
df = pd.DataFrame(data=dic)
df
# 将性别使用中文来标志
sex_dic = {
'male':'男',
'female':'女'
}
# map可以根据gender这组数据中的每一个元素根据字典表示的关系进行映射
df['gender'] = df['gender'].map(sex_dic)
df
# 给每一个英文名起一个中文名
name_dic = {
'tom': '张三',
'jerry': '李四',
'alex': '王五'
}
df['e_name'] = df['name'].map(name_dic)
df
4 数据运算
map函数对Series数据的运算处理(map作为Series的运算工具)
- 超过3000部分的钱缴纳50%的税,计算每个人的税后薪资
def after_sal(s): # 此处s参数表示每一个薪资的数据
# 该函数的调用次数取决于salary列中元素的个数
return s - (s - 3000) * 0.5
df['after_sal'] = df['salary'].map(after_sal)
df
# 改为匿名函数
df['salary'].map(lambda s: s - (s - 3000) * 0.5)
dic = {
'name': ['tom', 'jerry', 'alex', 'tom'],
'salary': [10000, 20000, 15000, 21000],
'gender': ['male', 'female', 'male', 'female'],
'hire_date': ['2020-01-10', '2021-11-11', '2022-12-12','2023-01-19',]
}
df = pd.DataFrame(data=dic)
df
# 将每一个员工的入职日期 +2 年
# 将 'hire_date' 转换为日期格式并加上2年
df['hire_date'] = pd.to_datetime(df['hire_date'])
df['hire_date_now'] = df['hire_date'].map(lambda x: x + pd.DateOffset(years=2))
df
dic = {
'name': ['tom', 'jerry', 'alex', 'tom'],
'salary': [10000, 20000, 15000, 21000],
'gender': ['male', 'female', 'male', 'female'],
'hire_date': ['2020-01-10', '2021-11-11', '2022-12-12','2023-01-19',]
}
df = pd.DataFrame(data=dic)
# 也可以使用函数
# 将每一个员工的入职日期 +2 年
def func(x):
year, month, day = x.split('-')
year = int(year) + 2
return str(year)+'-'+month+'-'+day
df['hire_date'] = df['hire_date'].map(func)
df
提示:apply也可以像map一样充当运算工具,不过apply运算效率要远远高于map。
因此在数据量级较大的时候可以使用apply。
dic = {
'name': ['tom', 'jerry', 'alex', 'tom'],
'salary': [10000, 20000, 15000, 21000],
'gender': ['male', 'female', 'male', 'female'],
'hire_date': ['2020-01-10', '2021-11-11', '2022-12-12','2023-01-19',]
}
df = pd.DataFrame(data=dic)
def func(x):
year, month, day = x.split('-')
year = int(year) + 2
return str(year)+'-'+month+'-'+day
df['hire_date'] = df['hire_date'].apply(func)
df
5 数据分组
- 数据分类处理的核心:
- groupby()函数
- groups属性查看分组情况
import pandas as pd
import warnings
warnings.filterwarnings('ignore')
df = pd.DataFrame({
'item':['Apple','Banana','0range','Banana','0range','Apple'],
'price':[4, 3, 3, 2.5, 4, 2],
'color':['red','yellow','yellow','green','green','green'],
'weight':[12,20,50,30,20,44]
})
df
# 计算每种水果的平均价格
df.groupby(by='item').groups # groups查看分组的结果
mean_price = df.groupby(by='item')['price'].mean()
mean_price
# 将平均价格转为字典
dic = mean_price.to_dict()
dic
# 将水果的平均价格汇总到原始的数据中
df['mean_price'] = df['item'].map(dic)
df
# 计算不同颜色水果的最大重量
max_weight = df.groupby(by='color')['weight'].max()
max_weight
dic = max_weight.to_dict()
df['color'].map(dic)
# 将不同水果的最大重量汇总到原始的数据中
df['max_weight'] = df['color'].map(dic)
df
# 计算每种水果最大价格和最低价格的差值
def func(x): # 参数x:一组水果的所有价格
return x.max() - x.min()
df.groupby(by='item')['price'].transform(func)
- agg实现对分组后的结果进行多种不同形式的聚合操作
# 求每种水果的最大和最小价格
df.groupby(by='item')['price'].agg(['max', 'min', 'mean', 'sum'])
6 透视表pivot_table函数
- 对不同字段不同形式的聚合操作
# 计算每种水果的平均价格
# 参数index:表示分组的条件
# 参数values:表示对哪些字段进行聚合操作
# 参数aggfunc:用户指定聚合形式
df.pivot_table(index='item', values='price', aggfunc='mean')
# 计算不同颜色水果的最大、最小和平均重量
df.pivot_table(index='color', aggfunc={'weight': 'max', 'weight': 'min', 'weight': 'mean',})
# 计算不同颜色水果的最大、最小和平均重量
df.pivot_table(index='color', values='weight', aggfunc=['max', 'min', 'mean'])