强大的数据处理模块Pandas,可以解决数据的预处理工作,如数据类型的转换、缺失值的处理、描述性统计分析和数据的汇总等
一、序列与数据框的构造
Pandas模块的核心操作对象为序列和数据框。序列指数据集中的一个字段,数据框为至少含有两个字段(或序列)的数据集。
1.构造序列
通过列表、元祖、Numpy中的一维数组构造序列均如序列1,第一列为序列的行索引(行号),自动从0开始;第二列为序列的实际值
通过字典构造不同,第一列为具体的行名称,对应字典中的键,第二列为序列的实际值,对应字典中的值。
还可以通过数据框中的某一列构建
2.序列元素的获取
对一维数组的索引方法和数学以及统计函数都可以用到序列上,但序列有特有的处理方法。
对通过字典类型构建的,可用行号,也可用行名称索引
如果需要对序列做数学函数计算,首选numpy模块
如果需要对序列做统计函数计算,首选序列的方法
3.构造数据框
数据框实际是一个数据集,行代表每一条观测,列代表各个变量。在一个数据库中可以存放不同数据类型的序列,而数组和序列只能存放同质数据。
手工构造数据框的话,首选字典方法,因为其他方法构造没有具体的变量名
还可以通过外部数据的读取构造
二、外部数据的读取
1.文本文件的读取
使用Pandas中的read_table函数或者read_csv函数
filepath_or_buffer:指定txt文件或csv文件所在的具体路径;
sep:指定原数据集中各字段之间的分隔符,默认为Tab制表符;
header:是否需要将原数据集中的第一行作为表头,默认将第一行用作字段名称;
names:如果原数据集中没有字段,可以通过该参数在数据读取时给数据框添加具体的表头;
index_col:指定原数据集中的某些列作为数据框的行索引(标签);
usecols:指定需要读取原数据集中的哪些变量名;
dtype:读取数据时,可以为原数据集的每个字段设置不同的数据类型;
converters:通过字典格式,为数据集中的某些字段设置转换函数
skiprows:数据读取时,指定需要跳过原数据集开头的行数;
skipfooter:数据读取时,指定需要跳过原数据集末尾的行数;
nrows:指定读取数据的行数;
na_values:指定原数据集中哪些特征的值作为缺失值;
skip_blank_lines:读取数据时是否需要跳过原数据集中的空白行,默认为True;
parse_dates:如果参数值为True,则尝试解析数据框的行索引;如果参数为列表,则尝试解析对应的日期列;如果参数为嵌套列表,则将某些列合并为日期列;如果参数为字典,则解析对应的列(即字典中的值),并生成新的字段名(即字典中的键);
thousands:指定原始数据集中的千分位符;
comment:指定注释符,在读取数据时,如果碰到行首指定的注释符,则跳过改行;
encoding:如果文件中含有中文,有时需要指定字符编码;
a=pd.read_table("F:\第5章 Python数据处理工具--Pandas\第五章 Python数据处理工具--Pandas\data_test01.txt",sep=",",skiprows=2,skipfooter=3,comment="#",encoding="utf8",thousands="&",parse_dates={"birthday":[0,1,2]})
a
原数据集用逗号分隔每一列,则改变sep参数,合并新字段birthday,comment参数指定跳过的特殊行,含有中文的重新编码,千分位符为了保证数值型数据的正常读入
2.电子表格的读取
使用read_excel函数
io:指定电子表格的具体路径;
sheetname:指定需要读取电子表格中的第几个Sheet,可以传递整数也可以传递具体的Sheet名称;
header:是否需要将数据集的第一行用作表头,默认为是需要的;
skiprows:读取数据时,指定跳过的开始行数;
skip_footer:读取数据是,指定跳过的末尾行数;
index_col:指定哪些列用作数据框的行索引(标签);
names:如果原数据集中没有字段,可以通过该参数在数据读取时给数据框添加具体的表头;
parse_cols:指定需要解析的字段;
parse_dates:如果参数值为True,则尝试解析数据框的行索引;如果参数为列表,则尝试解析对应的日期列;如果参数为嵌套列表,则将某些列合并为日期列;如果参数为字典,则解析对应的列(即字典中的值),并生成新的字段名(即字典中的键);
na_values:指定原始数据中哪些特殊值代表了缺失值;
thousands:指定原始数据集中的千分位符;
convert_float:默认将所有的数值型字段转换为浮点型字段;
converters:通过字典的形式,指定某些列需要转换的形式;
b=pd.read_excel(io="F:\第5章 Python数据处理工具--Pandas\第五章 Python数据处理工具--Pandas\data_test02.xlsx",header=None,converters={0:str},names=['ID',"name",'color',"price"])
b
对于第一列,实际上是字符型,为了避免数据读入时自动变成数值型字段,需要用converts参数
3.数据库数据的读取
需要先通过cmd命令输入pip install pymysql或者pysmsql(分别对应MYSQL和SQL Server)
CASE1:pymysql中的connect
host:指定需要访问的MySQL服务器;
user:指定访问MySQL数据库的用户名;
password:指定访问MySQL数据库的密码;
database:指定访问MySQL数据库的具体库名;
port:指定访问MySQL数据库的端口号;
charset:指定读取MySQL数据库的字符集,如果数据库表中含有中文,一般可以尝试将该参数设置为“utf8”或“gbk”;
CASE2:pymssql中的connect
参数含义也是一致的,所不同的是pymysql模块中connect函数的host参数表示需要访问的服务器,而pymssql函数中对应的参数是server
以MYSQL举例:
# 读入MySQL数据库数据
# 导入第三方模块
import pymysql
# 连接MySQL数据库
conn = pymysql.connect(host='localhost', user='root', password='test',
database='test', port=3306, charset='utf8')
# 读取数据
user = pd.read_sql('select * from topy', conn)
# 关闭连接
conn.close()
# 数据输出
user
三、数据类型转换及描述统计
涉及如何了解数据,例如读入数据规模如何、各个变量属于什么类型、重要统计指标所对应的值、离散变量各唯一值的频次统计等等。
以某平台二手车信息为例:
#数据读取
cars=pd.read_table("F:\sec_cars.csv",sep=",")
#预览数据前5行
cars.head()
后5行数据为tail
#查看数据行列数
print(cars.shape)
#查看变量数据类型
print(cars.dtypes)
object指字符型,但车牌时间应该为日期型,新车价格应该为浮点型,下面修改
日期类型一般通过pandas模块中的to_datetime函数,format参数可直接设置格式
#修改车牌时间
cars.Boarding_time=pd.to_datetime(cars.Boarding_time,format="%Y年%m月")
新车价格中含有“万”字,因此不能直接转为数据类型。需要三步走,首先通过str方法将该字段转换成字符串,然后通过切片手段,将“万”字剔除,最后运用astype方法,实现数据类型的转换。
#修改新车价格
cars.New_price=cars.New_price.str[:-1].astype("float")
#重新查看数据类型
cars.dtypes
接下来,需要对数据做到心中有数,即通过基本的统计量(如最小值、均值、中位数、最大值等)描述出数据中所有数值型变量的特征。关于数据的描述性分析可以使用describe方法:
#数据的描述性统计
cars.describe()
进一步了解数据的形状分布,如数据是否有偏以及是否属于“尖峰厚尾”的特征,一次性统计数值型变量的偏度和峰度
columns方法用于返回数据集的所有变量名,通过布尔索引和切片方法获得所有的数值型变量
#筛选出所有数值型变量
num_variables=cars.columns[cars.dtypes!="object"][1:]
num_variables
在自定义函数中,运用到了计算偏度的skew方法和计算峰度的kurt方法,然后将计算结果组合到序列中
#自定义函数,计算偏度和峰度
def skew_kurt(x):
skewness=x.skew()
kurtsis=x.kurt()
return pd.Series([skewness,kurtsis],index=["skew","kurt"])
使用apply方法,该方法的目的就是对指定轴(axis=0,即垂直方向的各列)进行统计运算(运算函数即自定义函数)
cars[num_variables].apply(func=skew_kurt,axis=0)
以上的统计分析全都是针对数值型变量的,对于字符型变量(如二手车品牌Brand、排放量Discharge等)可以使用describe方法,所不同是,需要将“object”以列表的形式传递给include参数
#离散型变量的统计描述
cars.describe(include=["object"])
如上结果包含离散变量的四个统计值,分别是非缺失观测数、唯一水平数、频次最高的离散值和具体的频次。以二手车品牌为例,一共有10,984辆二手车,包含104种品牌,其中别克品牌最多,高达1,346辆。
进一步需要统计的是各个离散值的频次,甚至是对应的频率。以二手车品的标准排量Discharge为例
#离散变量频次统计
Freq=cars.Discharge.value_counts()
#总记录,总频次
cars.shape[0]
Freq_ratio = Freq/cars.shape[0]
Freq_df = pd.DataFrame({'Freq':Freq,'Freq_ratio':Freq_ratio})
Freq_df.head()
构成的数据框包含两列,分别是二手车各种标准排量对应的频次和频率,数据框的行索引(标签)就是二手车不同的标准排量。如果读者需要把行标签设置为数据框中的列,可以使用reset_index方法,inplace参数设置为True,表示直接对原始数据集进行操作,影响到原数据集的变化,否则返回的只是变化预览
#将行索引重设为变量
Freq_df.reset_index(inplace=True)
#重新查看数据框
Freq_df.head()
四、字符与日期数据的处理
如何基于数据框操作字符型变量,以及有关日期型数据的处理,例如如何从日期型变量中取出年份、月份、星期几等,如何计算两个日期间的时间差
df=pd.read_excel("F:\data_test03.xlsx")
#查看数据前五行
df.head()
#查看数据类型
df.dtypes
下面修改生日birthday为日期型,电话号码tel为字符串
#修改birthday
df.birthday=pd.to_datetime(df.birthday,format="%Y/%m/%d")
#修改tel
df.tel=df.tel.astype("str")
#重新查看数据类型
df.dtypes
对于年龄和工龄的计算,需要将当前日期与出生日期和开始工作日期作减法运算,而当前日期的获得,则使用了Pandas子模块datetime中的today函数。由于计算的是相隔的年数,所以还需进一步取出日期中的年份dt.year
#根据出生日期新增年龄
df["age"]=pd.datetime.today().year-df.birthday.dt.year
#根据开始工作日期新增工龄
df["workage"]=pd.datetime.today().year-df.start_work.dt.year
将手机号tel的中间四位隐藏起来,是字符串中的替换法(replace),由于替换法所处理的对象都是变量中的每一个观测,属于重复性工作,所以考虑使用序列的apply方法。需要注意的是,apply方法中的func参数,都是使用匿名函数,对于隐藏手机号中间四位的思路就是用星号替换手机号的中间四位
#隐藏电话号码中间4位
df.tel = df.tel.apply(func = lambda x : x.replace(x[3:7],"****"))
对于邮箱域名的获取,是字符串中的分割法(split),其思路就是按照邮箱中的@符风格,然后取出第二个元素(即列表索引为1)
#取出邮箱域名,新增一列
df["email_domain"]=df.email.apply(func = lambda x: x.split("@")[1])
从other变量中获取人员的专业信息,使用了字符串的正则表达式,不管是字符串“方法”还是字符串正则,在使用前都需要对变量使用一次str方法。findall是匹配查询函数,然后正则符号(.*?)用于分组,默认返回括号内的匹配内容
# 取出人员的专业信息
df['profession'] = df.other.str.findall('专业:(.*?),')
删除数据集中的某些变量,可以使用数据框的drop方法。该方法接受的第一个参数就是被删除的变量列表,尤其要注意的是,需要将axis参数设置为1,因为默然drop方法是用来删除数据框中的行记录
# 去除birthday、start_work和other变量
df.drop(['birthday','start_work','other'], axis = 1, inplace = True)
#重新查看前五行
df.head()
针对日期型数据罗列一些常用的“方法”:
五、常用的数据清洗方法
数据集是否存在重复、是否存在缺失、数据是否具有完整性和一致性、数据中是否存在异常值等
1.重复观测处理
df=pd.read_excel("F:\data_test04.xlsx")
#查看数据
df
any函数表示的是在多个条件判断中,只要有一个条件为True,则any函数的结果就为True
#检测是否有重复观测
any(df.duplicated())
删除重复项,nplace=True就表示直接在原始数据集上作操作
# 删除重复项
df.drop_duplicates(inplace = True)
df
原先的10行观测,尽管排重后得到7行观测,被删除的行号为3、8和9、
2.缺失值处理
导致观测的缺失可能有两方面原因,一方面是人为原因(如记录过程中的遗漏、个人隐私而不愿透露等),另一方面是机器或设备的故障所导致(如断电或设备老化等原因)。
一般而言,当遇到缺失值(Python中用NaN表示)时,可以采用三种方法处置,分别是删除法、替换法和插补法。删除法是指当缺失的观测比例非常低时(如5%以内),直接删除存在缺失的观测,或者当某些变量的缺失比例非常高时(如85%以上),直接删除这些缺失的变量;替换法是指用某种常数直接替换那些缺失值,例如,对连续变量而言,可以使用均值或中位数替换,对于离散变量,可以使用众数替换;插补法是指根据其他非缺失的变量或观测来预测缺失值,常见的插补法有回归插补法、K近邻插补法、拉格朗日插补法等。
CASE1:删除法
df=pd.read_excel("F:\data_test05.xlsx")
#查看是否有缺失数据
any(df.isnull())
分别使用两种方法实现数据集中缺失值的处理,行删除法,即将所有含缺失值的行记录全部删除,使用dropna方法;变量删除法,由于原数据集中age变量的缺失值最多,所以使用drop方法将age变量删除
#行删除法
df.dropna()
#变量删除法
df.drop("age",axis=1)
CASE2:替换法
缺失值的替换需要借助于fillna方法,该方法中的method参数,可以接受'ffill'和'bfill'两种值,分别代表前向填充和后向填充。前向填充是指用缺失值的前一个值替换(如左图所示),而后向填充则表示用缺失值的后一个值替换(如右图所示)。右图中的最后一个记录仍包含缺失值,是因为后向填充法找不到该缺失值的后一个值用于替换。在作者看来,缺失值的前向填充或后向填充一般适用于时间序列型的数据集,因为这样的数据前后具有连贯性,而一般的独立性样本并不适用该方法。
# 替换法之前向替换
df.fillna(method = 'ffill')
# 替换法之后向替换
df.fillna(method = 'bfill')
另一种替换手段,仍然是使用fillna方法,只不过不再使用method参数,而是使用value参数。使用一个常数0替换所有的缺失值。
#使用常数0替换
df.fillna(value=0)
或者采用更加灵活的替换方法,即分别对各缺失变量使用不同的替换值(需要采用字典的方式传递给value参数),性别使用众数替换,年龄使用均值替换,收入使用中位数替换。
#使用统计值替换
df.fillna(value={'gender':df.gender.mode()[0], 'age':df.age.mean(), 'income':df.income.median()})
需要说明的是,如上代码并没有实际改变df数据框的结果,因为dropna、drop和fillna方法并没有使inplace参数设置为True。读者可以在实际的学习和工作中挑选一个适当的缺失值处理方法,然后将该方法中的inplace参数设置为True,进而可以真正的改变你所处理的数据集。
3.异常值处理
异常值是指那些远离正常值的观测,即“不合群”观测。导致异常值的出现一般是人为的记录错误或者是设备的故障等,异常值的出现会对模型的创建和预测产生严重的后果。当然异常值也不一定都是坏事,有些情况下,通过寻找异常值就能够给业务带来良好的发展,如销毁“钓鱼”网站、关闭“薅羊毛”用户的权限等。
对于异常值的检测,一般采用两种方法:
一种是n个标准差法,标准差法的判断公式是outlinear>|x ̅±nσ|,其中x ̅为样本均值,σ为样本标准差,当n=2时,满足条件的观测就是异常值,当n=3时,满足条件的观测就是极端异常值;
另一种是箱线图判别法,箱线图的判断公式是outlinear>Q3+ nIQR或者outlinear
这两种方法的选择标准如下,如果数据近似服从正态分布时,优先选择n个标准差法,因为数据的分布相对比较对称;否则优先选择箱线图法,因为分位数并不会受到极端值的影响。当数据存在异常时,一般可以使用删除法将异常值删除(前提是异常观测的比例不能太大)、替换法(可以考虑使用低于判别上限的最大值或高于判别下限的最小值替换、使用均值或中位数替换等)。
sun=pd.read_table("F:\sunspots.csv",sep=",")
sun.head()
# 异常值检测之标准差法
xbar = sunspots.counts.mean()
xstd = sunspots.counts.std()
print('标准差法异常值上限检测:\n',any(sunspots.counts > xbar + 2 * xstd))
print('标准差法异常值下限检测:\n',any(sunspots.counts < xbar - 2 * xstd))
# 异常值检测之箱线图法
Q1 = sunspots.counts.quantile(q = 0.25)
Q3 = sunspots.counts.quantile(q = 0.75)
IQR = Q3 - Q1
print('箱线图法异常值上限检测:\n',any(sunspots.counts > Q3 + 1.5 * IQR))
print('箱线图法异常值下限检测:\n',any(sunspots.counts < Q1 - 1.5 * IQR))
不管是标准差检验法还是箱线图检验法,都发现太阳黑子数据中存在异常值,而且异常值都是超过上限临界值的。
接下来,通过绘制太阳黑子数量的直方图和核密度曲线图,用于检验数据是否近似服从正态分布,进而选择一个最终的异常值判别方法:
#导入绘图模块
import matplotlib.pyplot as plt
#设置绘图风格
plt.style.use("ggplot")
#绘制直方图
sun.counts.plot(kind="hist",bins=30,normed=True)
#绘制核密度图
sun.counts.plot(kind="kde")
#图形展现
plt.show()
不管是直方图还是核密度曲线,所呈现的数据分布形状都是有偏的,并且属于右偏。基于此,这里就选择箱线图法用以判定太阳黑子数据中的那些异常值。接下来要做的就是选用删除法或替换法来处理这些异常值
此处使用低于判别上限的最大值或高于判别下限的最小值替换
# 箱线图中的异常值判别上限
UL = Q3 + 1.5 * IQR
print('判别异常值的上限临界值:\n',UL)
# 从数据中找出低于判别上限的最大值
replace_value = sun.counts[sun.counts < UL].max()
print('用以替换异常值的数据:\n',replace_value)
# 替换超过判别上限异常值
sun.counts[sun.counts > UL] = replace_value
如果使用箱线图法判别异常值,则认定太阳黑子数目一年内超过148.85时即认为是异常值年份,对于这些年份的异常值使用141.7替换。
六、数据子集的获取
通常,在Pandas模块中实现数据框子集的获取可以使用iloc、loc和ix三种“方法”,这三种方法既可以对数据行作筛选,也可以实现变量的挑选,它们的语法可以表示成[rows_select, cols_select]。
iloc只能通过行号和列号进行数据的筛选,读者可以将iloc中的“i”理解为“integer”,即只能向[rows_select, cols_select]指定整数列表。该索引方式与数组的索引方式类似,都是从0开始、可以间隔取号、对于切片仍然无法取到上限。
loc要比iloc灵活一些,读者可以将loc中的“l”理解为“label”,即可以向[rows_select, cols_select]指定具体的行标签(行名称)和列标签(字段名),注意,这里是标签不再是索引。而且,还可以将rows_select指定为具体的筛选条件,在iloc中是无法做到的。
ix是iloc和loc的混合,读者可以将ix理解为“mix”,该“方法”吸收了iloc和loc的优点,使数据框子集的获取更加的灵活。
1.原始数据的行号与行标签(名称)一致
# 构造数据集
df1 = pd.DataFrame({'name':['张三','李四','王二','丁一','李五'],
'gender':['男','女','女','女','男'],
'age':[23,26,22,25,27]}, columns = ['name','gender','age'])
df
取出数据集中间三行,并且返回姓名和年龄指定的两列
①iloc即索引的方法
df1.iloc[1:4,[0,2]]
②loc不再是索引,直接对应行名称
df1.loc[1:3, ['name','age']]
③ix效果同loc,但对变量名的筛选可用列号也可用具体变量名称
df1.ix[1:3,[0,2]]
#或者
df1.ix[1:3,['name','age']]
2.原始数据的行号与行标签(名称)不一致/没有行号
# 将员工的姓名用作行标签
df2 = df1.set_index('name')
df2
取出数据集的中间三行
①iloc即索引的方法
df2.iloc[1:4,:]
②loc使用行标签,不可再写行号
df2.loc[['李四','王二','丁一'],:]
③ix此时同iloc
df2.ix[1:4,:]
3.取出所有男性的性别和年龄
对某些列做条件筛选,只能使用loc和ix
df1.loc[df1.gender == '男',['name','age']]
df1.ix[df1.gender == '男',['name','age']]
七、透视表功能
pd.pivot_table(data, values=None, index=None, columns=None,
aggfunc='mean', fill_value=None, margins=False,
dropna=True, margins_name='All')
data:指定需要构造透视表的数据集;
values:指定需要拉入“数值”框的字段列表;
index:指定需要拉入“行标签”框的字段列表;
columns:指定需要拉入“列标签”框的字段列表;
aggfunc:指定数值的统计函数,默认为统计均值,也可以指定Numpy模块中的其他统计函数;
fill_value:指定一个标量,用于填充缺失值;
margins:bool类型参数,是否需要显示行或列的总计值,默认为False;
dropna:bool类型参数,是否需要删除整列为缺失的字段,默认为True;
margins_name:指定行或列的总计名称,默认为All;
1.单个分组变量的均值统计
基于单个分组变量color的汇总统计(price的均值)
diamonds=pd.read_table("F:\diamonds.csv",sep=",")
diamonds.head()
# 单个分组变量的均值统计
pd.pivot_table(data = diamonds, index = 'color', values = 'price', margins = True, margins_name = '总计')
2.两个分组变量的列联表
对于列联表来说,行和列都需要指定某个分组变量,所以index参数和columns参数都需要指定一个分组变量。并且统计的不再是某个变量的均值,而是观测个数,所以aggfunc参数需要指定numpy模块中的size函数。通过这样的参数设置
# 两个分组变量的列联表
# 导入numpy模块
import numpy as np
pd.pivot_table(data = diamonds, index = 'clarity', columns = 'cut', values = 'carat',
aggfunc = np.size,margins = True, margins_name = '总计')
八、表之间的合并和连接
对于多表之间的纵向合并,则必须确保多表的列数和数据类型一致;对于多表之间的水平扩展,则必须保证多表要有共同的匹配字段。Pandas模块同样提供了关于多表之间的合并和连接操作函数,分别是concat函数和merge函数。
1.合并函数concat
pd.concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False, keys=None)
objs:指定需要合并的对象,可以是序列、数据框或面板数据构成的列表;
axis:指定数据合并的轴,默认为0,表示合并多个数据的行,如果为1,则表示合并多个数据的列;
join:指定合并的方式,默认为outer,表示合并所有数据,如果改为inner,表示合并公共部分的数据;
join_axes:合并数据后,指定保留的数据轴;
ignore_index:bool类型的参数,表示是否忽略原数据集的索引,默认为False,如果设为True,则表示忽略原索引并生成新索引;
keys:为合并后的数据添加新索引,用于区分各个数据部分;
# 构造数据集df1和df2
df1 = pd.DataFrame({'name':['张三','李四','王二'], 'age':[21,25,22], 'gender':['男','女','男']})
df2 = pd.DataFrame({'name':['丁一','赵五'], 'age':[23,22], 'gender':['女','女']})
# 数据集的纵向合并
pd.concat([df1,df2] , keys = ['df1','df2'])
为了区分合并后的df1数据集和df2数据集,代码中的concat函数使用了keys参数,如果再设置参数ignore_index为True,此时keys参数将不再有效
pd.concat([df1,df2] , keys = ['df1','df2'],ignore_index=True)
2.连接函数merge
函数的最大缺点是,每次只能操作两张数据表的连接,如果有n张表需要连接,则必须经过n-1次的merge函数使用
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'))
left:指定需要连接的主表;
right:指定需要连接的辅表;
how:指定连接方式,默认为inner内连,还有其他选项,如左连left、右连right和外连outer;
on:指定连接两张表的共同字段;
left_on:指定主表中需要连接的共同字段;
right_on:指定辅表中需要连接的共同字段;
left_index:bool类型参数,是否将主表中的行索引用作表连接的共同字段,默认为False;
right_index:bool类型参数,是否将辅表中的行索引用作表连接的共同字段,默认为False;
sort:bool类型参数,是否对连接后的数据按照共同字段排序,默认为False
suffixes:如果数据连接的结果中存在重叠的变量名,则使用各自的前缀进行区分;
# 构造数据集
df3 = pd.DataFrame({'id':[1,2,3,4,5],'name':['张三','李四','王二','丁一','赵五'],'age':[27,24,25,23,25],'gender':['男','男','男','女','女']})
df4 = pd.DataFrame({'Id':[1,2,2,4,4,4,5],'kemu':['科目1','科目1','科目2','科目1','科目2','科目3','科目1'],'score':[83,81,87,75,86,74,88]})
df5 = pd.DataFrame({'id':[1,3,5],'name':['张三','王二','赵五'],'income':[13500,18000,15000]})
# 三表的数据连接
# 首先df3和df4连接
merge1 = pd.merge(left = df3, right = df4, how = 'left', left_on='id', right_on='Id')
merge1
# 再将连接结果与df5连接
merge2 = pd.merge(left = merge1, right = df5, how = 'left')
merge2
如果需要将这三张表横向扩展到一张宽表中,需要经过两次的merge操作。如上代码所示,第一次merge连接了df3和df4,由于两张表的共同字段不一致,所以需要分别指定left_on和right_on的参数值;第二次merge连接了首次的结果和df5,此时并不需要指定left_on和right_on参数,是因为第一次的merge结果就包含了id变量,所以merge时会自动挑选完全一致的变量用于表连接。
九、分组聚合操作
在数据库中还有一种非常常见的操作就是分组聚合,即根据某些个分组变量,对数值型变量进行分组统计。以珠宝数据为例,统计各颜色和刀工组合下的珠宝数量、最小x、平均价格和最大深度
需结合使用Pandas模块中的groupby“方法”和aggregate“方法”
使用Pandas实现分组聚合需要分两步走,第一步是指定分组变量,可以通过数据框的groupby“方法”完成;第二步是对不同的数值变量计算各自的统计值,在第二步中,需要跟读者说明的是,必须以字典的形式控制变量名称和统计函数
# 通过groupby方法,指定分组变量
grouped = diamonds.groupby(by = ['color','cut'])
# 对分组变量进行统计汇总
result = grouped.aggregate({'color':np.size, 'x':np.min, 'price':np.mean, 'depth':np.max})
result
....
# 数据集重命名
result.rename(columns={'color':'counts','x':'min_x','price':'avg_price','depth':'max_depth'}, inplace=True)
result
...
分组变量color和cut成了数据框的行索引。如果需要将这两个行索引转换为数据框的变量名,可以使用数据框的reset_index方法
# 将行索引变量数据框的变量
result.reset_index(inplace=True)
result
...
总结
pandas模块
Series 生成序列类型的函数
DataFrame 生成数据框类型的函数
read_table 读取文本文件的函数,如txt、csv等
read_csv 读取文本文件的函数,如txt、csv等
read_excel 读取电子表格的函数
pymysql/pmssql模块
connect 数据库与Python的连接函数
close 关闭数据库与Python之间连接的“方法”
pandas模块
read_sql 读取数据库数据的函数
head/tail 显示数据框首/末几行的“方法”
shape 返回数据框行列数的“方法”
dtypes 返回数据框中各变量数据类型的“方法”
to_datetime 将变量转换为日期时间型的函数
astype 将变量转换为其他类型的“方法”
describe 统计性描述的“方法”
columns 返回数据框变量名的“方法”
index 返回数据框行索引的“方法”
apply 序列或数据框的映射“方法”
value_counts 序列值频次统计的“方法”
reset_index 将行索引转换为变量的“方法”
duplicated 检验观测是否重复的“方法”
drop_duplicates 删除重复项的“方法”
drop 删除变量名或观测的“方法”
dropna 删除缺失值的“方法”
fillna 填充缺失值的“方法”
quantile 统计序列分位数的“方法”
plot 序列或数据框的绘图“方法”
iloc/loc/ix 数据框子集获取的“方法”
pivot_table 构建透视表的函数
concat 实现多表纵向合并的函数
merge 实现两表水平扩展的函数
groupby 分组聚合时,指定分组变量的“方法”
aggregate 指定聚合统计的“方法”
rename 修改数据框变量名的“方法”