完整参考:
数据经过采集后通常会被存储到Word、Excel、JSON等文件或数据库中,从而为后期的预处理工作做好数据储备。数据获取是数据预处理的第一步操作,主要是从不同的渠道中读取数据。Pandas支持CSV、TXT、Excel、JSON这几种格式文件、HTML表格的读取操作,另外Python可借助第三方库实现Word与PDF文件的读取操作。本章主要为大家介绍如何从多个渠道中获取数据,为预处理做好数据准备。
read_csv(filepath_or_buffer,sep=',', delimiter=None,
header='infer', names=None, index_col=None, usecols=None,
squeeze=False, prefix=None, mangle_dupe_cols=True, encoding=None...)
filepath_or_buffe:表示文件的路径,可以取值为有效的路径字符串、路径对象或类似文件的对象。 sep:表示指定的分隔符,默认为“,”。 header:表示指定文件中的哪一行数据作为DataFrame类对象的列索引,默认为0,即第一行数据作为列索引。 names:表示DataFrame类对象的列索引列表,当names没被赋值时,header会变成0,即选取数据文件的第一行作为列名;当 names 被赋值,header 没被赋值时,那么header会变成None。如果都赋值,就会实现两个参数的组合功能。 encoding:表示指定的编码格式。 读取csv案例-指定sep,encoding,engine
案例
import pandas as pd
evaluation_data = pd.read_csv(
"phones.csv", sep=',',encoding='gbk',engine = 'python')
print(evaluation_data)
engine:使用的分析引擎。可以选择C或者是python。C引擎快但是Python引擎功能更加完备。
encoding:指定字符集类型,即编码,通常指定为’utf-8’
Excel文件(Excel 2007及以上版本的扩展名为.xlsx)是日常工作中经常使用的,该文件主要以工作表存储数据,工作表中包含排列成行和列的单元格。Excel文件中默认有3个工作表,用户可根据需要添加一定个数(因可用内存的限制)的工作表。
Pandas中使用read_excel()函数读取Excel文件中指定工作表的数据,并将数据转换成一个结构与工作表相似的DataFrame类对象。
pandas.read_excel(io, sheet_name=0, header=0, names=None, index_col=None,
usecols=None,squeeze=False, dtype=None, engine=None,converters=None,
true_values=None, false_values=None, skiprows=None, nrows=None,na_values=None,
parse_dates=False, date_parser=None,thousands=None, comment=None,
skipfooter=0,convert_float=True,**kwds)
sheet_name:表示要读取的工作表,默认值为0。 header:表示指定文件中的哪一行数据作为DataFrame类对象的列索引。 names:表示DataFrame类对象的列索引列表。
案例
data3 = pd.read_excel('Athletes_info.xlsx',sheet_name='Sheet1',header=0,engine='openpyxl')
上述代码会读取Athletes_info.xlsx文件中的Sheet1,header=0表示取第1行为列字段,采用openpyxl作为读取excel的引擎。
掌握read_json()函数的用法,可以熟练地使用该方法从JSON文件中获取数据
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它以简洁和清晰的层次结构来组织数据,易于被人们阅读和编写。JSON采用独立于编程语言的文本格式来存储数据,其文件的后缀名为.json,可通过文本编辑工具查看。
Pandas中使用read_json()函数读取JSON文件的数据,并将数据转换成一个DataFrame类对象。
pandas.read_json(path_or_buf=None, orient=None, typ='frame', dtype=None,
convert_axes=None, convert_dates=True, keep_default_dates=True,
numpy=False, precise_float=False, date_unit=None, encoding=None,
lines=False, chunksize=None, compression='infer')
案例
strtext='[{"ttery":"min","issue":"20130801-3391","code":"8,4,5,2,9","code1":"297734529","code2":null,"time":1013395466000},\
{"ttery":"min","issue":"20130801-3390","code":"7,8,2,1,2","code1":"298058212","code2":null,"time":1013395406000},\
{"ttery":"min","issue":"20130801-3389","code":"5,9,1,2,9","code1":"298329129","code2":null,"time":1013395346000},\
{"ttery":"min","issue":"20130801-3388","code":"3,8,7,3,3","code1":"298588733","code2":null,"time":1013395286000},\
{"ttery":"min","issue":"20130801-3387","code":"0,8,5,2,7","code1":"298818527","code2":null,"time":1013395226000}]'
df=pd.read_json(strtext,orient='records')
输出为:
from sqlalchemy import create_engine
import pandas as pd
# 配置数据库信息
db_info = {'user':'root',
'pwd':'XXXX', # 修改为你的密码
'host':'localhost',
'database':'XXX' # 这里我们事先指定了数据库,后续操作只需要表即可
}
#这里直接使用pymysql连接,echo=True,会显示在加载数据库所执行的SQL语句。
engine = create_engine('mysql+pymysql://%(user)s:%(pwd)s@%(host)s/%(database)s?charset=utf8' % db_info,encoding='utf-8')
# 执行sql查询,并把数据库连接作为参数传进来
data = pd.read_sql('SELECT dep_id, dep_name FROM dep_info', con = engine)
data
数据清理概述
缺失值的检测与处理 重复值的检测与处理 异常值的检测与处理
缺失值的检测可以采用isnull()、notnull()、isna()和notna()方法的用法,可以熟练地使用这些方法来检测缺失值。
isnull()、notnull()、isna()和notna()方法均会返回一个由布尔值组成、与原对象形状相同的新对象
其中isnull()和isna()方法的用法相同,它们会在检测到缺失值的位置标记True;
notnull()和notna()方法的用法相同,它们会在检测到缺失值的位置标记False。
为避免包含缺失值的数据对分析预测结果产生一定的偏差,缺失值被检测出来之后一般不建议保留,而是选择适当的手段给予处理。缺失值的常见处理方式有三种:删除缺失值、填充缺失值和插补缺失值,pandas中为每种处理方式均提供了相应的方法。
pandas中提供了删除缺失值的方法dropna(),dropna()方法用于删除缺失值所在的一行或一列数据,并返回一个删除缺失值后的新对象。
DataFrame.dropna(axis=0, how='any', thresh=None, subset=None,inplace=False)
axis:表示是否删除包含缺失值的行或列。 how:表示删除缺失值的方式。 thresh:表示保留至少有N个非NaN值的行或列。 subset:表示删除指定列的缺失值。 inplace:表示是否操作原数据。
pandas中提供了填充缺失值的方法fillna(),fillna()方法既可以使用指定的数据填充,也可以使用缺失值前面或后面的数据填充。
DataFrame.fillna(value=None, method=None, axis=None, inplace=False,
limit=None, downcast=None)
method:表示填充的方式,默认值为None。该参数还支持 'pad’或’ffill’和’backfill’或’bfill’几种取值,其中’pad’或’ffill’表示将最后一个有效值向后传播,也就是说使用缺失值前面的有效值填充缺失值;'backfill’或’bfill’表示将最后一个有效值向前传播,也就是说使用缺失值后面的有效值填充缺失值。 limit:表示可以连续填充的最大数量。
pandas中提供了插补缺失值的方法interpolate(),interpolate() 会根据相应的插值方法求得的值进行填充。
DataFrame.interpolate(method='linear', axis=0, limit=None, inplace=False,
limit_direction=None, limit_area=None, downcast=None, **kwargs)
method:表示使用的插值方法,该参数支持’linear’(默认值)、‘time’、 ‘index’、‘values’、 ‘nearest’ 、'barycentric’共6种取值,其中’linear’代表采用线性插值法进行填充;'time’代表根据时间长短进行填充;‘index’、'values’代表采用索引的实际数值进行填充;'nearest’代表采用最临近插值法进行填充;'barycentric’代表采用重心坐标插值法进行填充。 limit_direction:表示按照指定方向对连续的NaN进行填充。
import pandas as pd
import numpy as np
# 创建DataFrame
na_df = pd.DataFrame({'A':[1, 2, np.NaN, 4],
'B':[3, 4, 4, 5],
'C':[5, 6, 7, 8],
'D':[7, 5, np.NaN, np.NaN]})
# 使用isna()方法检测na_df中是否存在缺失值
na_df.isna()
# 计算每列缺失值的总和
na_df.isnull().sum()
# 看看缺失值所在的行
na_df[na_df.isnull().T.any()]
# 删除缺失值 -- 将缺失值出现的行全部删掉
na_df.dropna()
# 保留至少有3个非NaN值的行
na_df.dropna(thresh=3)
# 缺失值补全|整体填充 将全部缺失值替换为 *
na_df.fillna("*")
pandas中使用duplicated()方法来检测数据中的重复值。
DataFrame.duplicated(subset=None, keep='first')
subset:表示识别重复项的列索引或列索引序列,默认标识所有的列索引。 keep:表示采用哪种方式保留重复项,该参数可以取值为’first’(默认值)、 'last '和 ‘False’,其中’first’代表删除重复项,仅保留第一次出现的数据项;'last '代表删除重复项,仅保留最后一次出现的数据项;'False’表示所有相同的数据都被标记为重复项。 duplicated()方法检测完数据后会返回一个由布尔值组成的Series类对象,该对象中若包含True,说明True对应的一行数据为重复项。
重复值的一般处理方式是删除,pandas中使用drop_duplicates()方法删除重复值。
DataFrame.drop_duplicates(subset=None, keep='first', inplace=False,
ignore_index=False)
keep:表示采用哪种方式保留重复项,该参数可以取值为’first’(默认值)、 'last ‘和’False’,其中’first’代表删除重复项,仅保留第一次出现的数据项;'last '代表删除重复项,仅保留最后一次出现的数据项;'False’表示删除所有的重复项。 inplace:表示是否放弃副本数据,返回新的数据,默认为False。 ignore_index:表示是否对删除重复值后的对象的行索引重新排序,默认为Flase。
创建DataFrame对象:
# 创建DataFrame对象
import pandas as pd
import numpy as np
df = pd.DataFrame({'name': ['刘婷婷', '王淼', '彭岩', '刘华', '刘华', '周华'],
'age': [24, 23, 29, 22, 22, 27],
'height': [162, 165, 175, 175, 175, 178],
'gender': ['女', '女', '男', '男', '男', '男']})
# 检测df对象中的重复值
df.duplicated() # 返回boolean数组
# 查找重复值
# 将全部重复值所在的行筛选出来
df[df.duplicated()]
# 查找重复值|指定
# 上面是所有列完全重复的情况,但有时我们只需要根据某列查找重复值
df[df.duplicated(['gender'])]
# 删除全部的重复值
df.drop_duplicates()
# 删除重复值|指定
# 删除全部的重复值,但保留最后一次出现的值
df.drop_duplicates(keep = 'last')
异常值的检测可以采用 3σ原则 和 箱形图检测。
3σ原则,又称为拉依达原则,它是先假设一组检测数据只含有随机误差,对该组数据进行计算处理得到标准偏差,按一定概率确定一个区间,凡是超过这个区间的误差不属于随机误差而是粗大误差,含有粗大误差范围内的数据(视为异常值)应予以剔除。
3σ原则并不适用于任意数据集,而只适用于符合或近似正态分布的数据集。
正态分布也称高斯分布,是统计学中十分重要的概率分布,它有两个比较重要的参数:μ和σ,其中μ是遵从正态分布的随机变量(值无法预先确定仅以一定的概率取值的变量)的均值,σ是此随机变量的标准差。
结合正态分布曲线图,3σ原则在各区间所占的概率如下: 数值分布在(μ-σ,μ+σ)区间中的概率为68.2%。 数值分布在(μ-2σ,μ+2σ)区间中的概率为95.4%。 数值分布在(μ-3σ,μ+3σ)区间中的概率为99.7%。 大多数数值集中在(μ-3σ,μ+3σ)区间的概率最大,数值超出这个区间的概率仅占不到0.3%。所以,凡是误差超过(μ-3σ,μ+3σ)区间的数值均属于异常值。
箱形图是一种用于显示一组数据分散情况的统计图,它通常由上边缘、上四分位数、中位数、下四分位数、下边缘和异常值组成。箱形图能直观地反映出一组数据的分散情况,一旦图中出现离群点(远离大多数值的点),就认为该离群点可能为异常值。
Q3表示上四分位数,说明全部检测值中有四分之一的值比它大; Q1表示下四分位数,说明全部检测值中有四分之一的值比它小; IQR表示四分位数间距,即上四分位数Q3与下四分位数Q1之差,其中包含了一半检测值; 空心圆点表示异常值,该值的范围通常为小于Q1 – 1.5IQR或大于Q3 + 1.5IQR
为了能够直观地从箱形图中查看异常值,pandas中提供了两个绘制箱形图的函数:plot()和boxplot(),其中plot()函数用于根据Series和DataFrame类对象绘制箱形图,该箱形图中默认不会显示网格线; boxplot()函数用于根据DataFrame类对象绘制箱形图,该箱形图中默认会显示网格线。
数据集成期间的数据问题,包括:
实体识别 冗余属性识别 元组重复等
pandas中内置了许多能轻松地合并数据的函数与方法,通过这些函数与方法可以将Series类对象或DataFrame类对象进行符合各种逻辑关系的合并操作,合并后生成一个整合的Series或DataFrame类对象。基于这些方法实现主键合并数据、重叠合并数据和堆叠合并数据操作。
常用的合并数据的函数包括:
主键合并数据类似于关系型数据库的连接操作,主要通过指定一个或多个键将两组数据进行连接,通常以两组数据中重复的列索引为合并键。
pd.merge(left, right, how='inner', on=None, left_on=None,
right_on=None, left_index=False, right_index=False, sort=False,
suffixes='_x', '_y', copy=True, indicator=False, validate=None)
参数含义如下:
left,right:参与合并的Series或DataFrame类对象。 how:表示数据合并的方式,支持’inner’(默认值)、‘left’、‘right’、'outer’共4个取值。 on:表示left与right合并的键。 sort:表示按键对应一列的顺序对合并结果进行排序,默认为True。 how参数的取值‘inner’代表基于left与right的共有的键合并,类似于数据库的内连接操作;'left’代表基于left的键合并,类似于数据库的左外连接操作;'right’代表基于right的键合并,类似于数据库的右外连接操作;'outer’代表基于所有left与right的键合并,类似于数据库的全外连接操作。
堆叠合并数据类似于数据库中合并数据表的操作,主要沿着某个轴将多个对象进行拼接。
pandas.concat(objs, axis=0, join='outer', join_axes=None,
ignore_index=False, keys=None, levels=None, names=None,
verify_integrity=False, sort=None, copy=True)
参数含义如下:
join:表示合并的方式,可以取值为’inner’或’outer’(默认值),其中’inner’表示内连接,即合并结果为多个对象重叠部分的索引及数据,没有数据的位置填充为NaN;'outer’表示外连接,即合并结果为多个对象各自的索引及数据,没有数据的位置填充为NaN。 ignore_index:是否忽略索引,可以取值为True或False(默认值)。若设为True,则会在清除结果对象的现有索引后生成一组新的索引。
Pandas可以通过append实现纵向追加:
df1 = pd.DataFrame([[1, 2], [3, 4]], columns=list('AB'))
print(df1)
df2 = pd.DataFrame([[5, 6], [7, 8]], columns=list('AB'))
print(df2)
# 纵向追加
df1.append(df2, ignore_index=True)
join函数如下:
DataFrame.join(self, other, on=None, how=“left”, lsuffix="", rsuffix="", sort=False)
其中
other:DataFrame, Series, or list of DataFrame,另外一个dataframe, series,或者dataframe list。 on: 参与join的列,与sql中的on参数类似。 how: {‘left’, ‘right’, ‘outer’, ‘inner’}, default ‘left’, 与sql中的join方式类似。 lsuffix: 左DataFrame中重复列的后缀 rsuffix: 右DataFrame中重复列的后缀 sort: 按字典序对结果在连接键上排序
join方式为按某个相同列进行join:
score_df = pd.DataFrame({'name': ['石申夫', '甘德', '乙', '甲'],
'age': [18, 28, 24, 36]})
score1_df = pd.DataFrame({'name': ['张衡', '石申夫', '乙', '甘德'],
'score': ['A', 'B', 'C', 'B']})
score_df.set_index('name', inplace=True) # 设置索引 可以尝试如果不设置会怎么样
score1_df.set_index('name', inplace=True) # 设置索引
score_df.join(score1_df, on='name')
输出为:
pandas包中,进行数据合并有join()、merge()、concat(), append()四种方法。它们的区别是:
df.join() 相同行索引的数据被合并在一起,因此拼接后的行数不会增加(可能会减少)、列数增加; df.merge()通过指定的列索引进行合并,行列都有可能增加;merge也可以指定行索引进行合并; pd.concat()通过axis参数指定在水平还是垂直方向拼接; df.append()在DataFrame的末尾添加一行或多行;大致等价于pd.concat(df1,df2,axis=0,join=‘outer’)。 join 最简单,主要用于基于索引的横向合并拼接 merge 最常用,主要用于基于指定列的横向合并拼接 concat最强大,可用于横向和纵向合并拼接 append,主要用于纵向追加
数据变换的常见处理方式包括:
数据标准化处理 数据离散化处理 数据泛化处理
分组与聚合是常见的数据变换操作
分组指根据分组条件(一个或多个键)将原数据拆分为若干个组; 聚合指任何能从分组数据生成标量值的变换过程,这一过程中主要对各分组应用同一操作,并把操作后所得的结果整合到一起,生成一组新数据。
下面通过一个例子说明分组聚合的过程:
掌握分组与聚合的过程,可以熟练地groupby()、agg()、transfrom()和apply()方法实现分组与聚合操作
pandas中使用groupby()方法根据键将原数据拆分为若干个分组。
groupby(by=None, axis=0, level=None, as_index=True, sort=True,
group_keys=True, squeeze=<object object>, observed=False, dropna=True)
by:表示分组的条件,可以取值为字符串、列表、字典或Series、函数等。 axis:表示分组操作的轴编号,可以是0或1。该参数的默认值为0,代表沿列方向操作。 level:表示标签索引所在的级别,默认为None。 as_index:表示聚合后新数据的索引是否为分组标签的索引,默认为True。 sort:表示是否对分组索引进行排序,默认为True。 group_keys:表示是否显示分组标签的名称,默认为True。
分组+自定义聚合:
# 分组+自定义聚合
import pandas as pd
df_obj = pd.DataFrame({"key":["C", "B", "C", "A", "B", "B", "A", "C", "A"],
"data":[2, 4, 6, 8, 10, 1, 3, 5, 7]})
print(df_obj)
df_obj[['key','data']].groupby(by="key").max()
输出为:
pandas中可通过多种方式实现聚合操作,除前面介绍过的内置统计方法之外,还包括agg()、transfrom()和apply()方法。
初始化聚合所需的DF:
# 初始化分组DF
import pandas as pd
df_obj = pd.DataFrame({'a': [0, 1, 2, 3, 4, 5],
'b': [1, 7, 13, 19, 25, 31],
'c': [1, 8, 14, 20, 26, 32],
'd': [2, 9, 15, 21, 27, 33],
'e': [2, 10, 16, 22, 28, 34],
'f': [2, 2, 2, 3, 3, 2]})
print(df_obj)
df_obj.groupby(by='f').agg({'a':'count'})
输出为:
transfrom()方法能对分组应用灵活的运算操作,同时可使聚合前与聚合后的数据结构保持一致。
# 初始化分组DF
import pandas as pd
df_obj = pd.DataFrame({'a': [0, 6, 12, 18, 24, 30],
'b': [1, 7, 13, 19, 25, 31],
'c': [1, 8, 14, 20, 26, 32],
'd': [2, 9, 15, 21, 27, 33],
'e': [2, 10, 16, 22, 28, 34],
'f': [2, 2, 2, 3, 3, 2]})
df_obj['a_max'] = df_obj[['a','f']].groupby(by=['f']).transform('max')
输出为:
apply()方法既能直接接收内置方法,又可以接收自定义的函数。
与前几种聚合方式相比,使用apply()方法聚合数据的操作更灵活,它可以代替前两种聚合完成基础操作,另外也可以解决一些特殊聚合操作。
apply(func, *args, **kwargs)
func:表示应用于各分组的函数或方法。 *args和**kwargs :表示传递给func的位置参数或关键字参数。
案例
# 初始化分组DF
import pandas as pd
df_obj = pd.DataFrame({'a': [0, 6, 12, 18, 24, 30],
'b': [1, 7, 13, 19, 25, 31],
'c': [1, 8, 14, 20, 26, 32],
'd': [2, 9, 15, 21, 27, 33],
'e': [2, 10, 16, 22, 28, 34],
'f': [2, 2, 2, 3, 3, 2]})
print(df_obj)
# 自定义函数,用于计算每个数据除以10的余数
def div_hun(df):
return df.iloc[:, :] % 10
df_obj.groupby(by=['f']).apply(div_hun)
输出为:
哑变量又称虚拟变量、名义变量等,它是人为虚设的变量,用来反映某个变量的不同类别,常用的取值为0和1。需要说明的是,0和1并不代表数量的多少,而代表不同的类别。
假设变量“职业”有司机、学生、导游、工人、教师共5个类别,这5个类别分别有0和1两种取值,0代表非此种类别,1代表此种类别。
实现哑变量的方法:
pandas中使用get_dummies()函数对类别数据进行哑变量处理,并在处理后返回一个哑变量矩阵。
get_dummies(data, prefix=None, prefix_sep='_', dummy_na=False,
columns=None, sparse=False, drop_first=False, dtype=None)
data:表示待处理的类别数据,可以是数组、DataFrame类或Series类对象。 prefix:表示列索引名称的前缀,默认为None。 prefix_sep:表示附加前缀的分隔符,默认为“_”。 columns:表示哑变量处理的列索引名称,默认为None。
案例
# 初始化DF
import pandas as pd
position_df = pd.DataFrame({'职业': ['工人', '学生', '司机', '教师', '导游']})
# 哑变量处理, 并给哑变量添加前缀
result = pd.get_dummies(position_df, prefix=['col'])
result
输出为:
掌握cut()函数的用法,可以熟练地使用过该函数实现面元划分操作
面元划分是指数据被离散化处理,按一定的映射关系划分为相应的面元(可以理解为区间),只适用于连续数据。连续数据又称连续变量,指在一定区间内可以任意取值的数据,该类型数据的特点是数值连续不断,相邻两个数值可作无限分割。
pandas中使用cut()函数能够实现面元划分操作,cut()函数会采用等宽法对连续型数据进行离散化处理。
cut(x, bins, right=True, labels=None, retbins=False, precision=3,
include_lowest=False, duplicates='raise', ordered=True)
x:表示面元划分的连续数据,可以取值为一维数组或Series类对象。 bins:表示划分面元的依据。 right:表示右端点是否为闭区间,默认为True。 precision:表示区间标签的精度,默认为3。 include_lowest:表示是否包含区间的左端点,默认为False。
cut()函数会返回一个Categorical类对象,该对象可以被看作一个包含若干个面元名称的数组,通过categories属性可以获取所有的分类,即每个数据对应的面元。
案例
import pandas as pd
ages = pd.Series([19, 21, 25, 55, 30, 45, 52, 46, 20])
bins = [0, 18, 30, 40, 50, 100]
# 使用cut函数划分年龄区间
cuts = pd.cut(ages, bins)
cuts
输出为:
import pandas as pd
ages = pd.Series([19, 21, 25, 55, 30, 45, 52, 46, 20])
bins = [0, 18, 30, 40, 50, 100]
# 使用cut函数划分年龄区间
cuts = pd.cut(ages, bins)
cuts
输出为:
pandas中可以使用stack()方法实现重塑分层索引操作。
stack(level=- 1, dropna=True)
level:表示索引的级别,默认为-1,即操作内层索引,若设为0,则会操作外层索引。 dropna:表示是否删除结果对象中存在缺失值的一行数据,默认为True。
同时还有一个stack的逆操作,unstack。两者的操作如下:
案例
初始化数据
import pandas as pd
import numpy as np
df = pd.DataFrame(np.array([[1,2,3],[4,5,6]]),
index=pd.Index(['A', 'B'], name='index_name'),
columns=pd.Index(['one', 'two', 'three'],
name='col_name'))
df
输出为:
使用stack列转行
# 重塑df,使之具有两层行索引
# 原来的列数据one, two, three就到了行上来了,形成多层索引。
# 注意这里:stack()操作后返回的对象是Series类型
result = df.stack()
result
输出为: