71.Pandas的应用-2
Pandas的应用-2
DataFrame的应用
创建DataFrame对象
通过二维数组创建DataFrame
对象
代码:
scores = np.random.randint(60, 101, (5, 3)) |
输出:
语文 数学 英语 |
通过字典创建DataFrame
对象
代码:
scores = { |
输出:
语文 数学 英语 |
读取 CSV 文件创建DataFrame
对象
可以通过pandas
模块的read_csv
函数来读取 CSV 文件,read_csv
函数的参数非常多,下面接受几个比较重要的参数。
sep
/delimiter
:分隔符,默认是,
。header
:表头(列索引)的位置,默认值是infer
,用第一行的内容作为表头(列索引)。index_col
:用作行索引(标签)的列。usecols
:需要加载的列,可以使用序号或者列名。true_values
/false_values
:哪些值被视为布尔值True
/False
。skiprows
:通过行号、索引或函数指定需要跳过的行。skipfooter
:要跳过的末尾行数。nrows
:需要读取的行数。na_values
:哪些值被视为空值。
代码:
df3 = pd.read_csv('2018年北京积分落户数据.csv', index_col='id') |
输出:
name birthday company score |
说明:如果需要上面例子中的 CSV 文件,可以通过下面的百度云盘地址进行获取,数据在《从零开始学数据分析》目录中。链接:https://pan.baidu.com/s/1rQujl5RQn9R7PadB2Z5g_g,提取码:e7b4。
读取Excel文件创建DataFrame
对象
可以通过pandas
模块的read_excel
函数来读取 Exce l文件,该函数与上面的read_csv
非常相近,多了一个sheet_name
参数来指定数据表的名称,但是不同于 CSV 文件,没有sep
或delimiter
这样的参数。下面的代码中,read_excel
函数的skiprows
参数是一个 Lambda 函数,通过该 Lambda 函数指定只读取 Excel 文件的表头和其中10%的数据,跳过其他的数据。
代码:
import random |
说明:如果需要上面例子中的 Excel 文件,可以通过下面的百度云盘地址进行获取,数据在《从零开始学数据分析》目录中。链接:https://pan.baidu.com/s/1rQujl5RQn9R7PadB2Z5g_g,提取码:e7b4。
输出:
购药时间 社保卡号 商品名称 销售数量 应收金额 实收金额 |
通过SQL从数据库读取数据创建DataFrame
对象
pandas
模块的read_sql
函数可以通过 SQL 语句从数据库中读取数据创建DataFrame
对象,该函数的第二个参数代表了需要连接的数据库。对于 MySQL 数据库,我们可以通过pymysql
或mysqlclient
来创建数据库连接,得到一个Connection
对象,而这个对象就是read_sql
函数需要的第二个参数,代码如下所示。
代码:
import pymysql |
提示:执行上面的代码需要先安装
pymysql
库,如果尚未安装,可以先在 Notebook 的单元格中先执行!pip install pymysql
,然后再运行上面的代码。上面的代码连接的是我部署在阿里云上的 MySQL 数据库,公网 IP 地址:47.104.31.138
,用户名:guest
,密码:Guest.618
,数据库:hrs
,表名:tb_emp
,字符集:utf8mb4
,大家可以使用这个数据库,但是不要进行恶意的访问。
输出:
ename job mgr sal comm dno |
基本属性和方法
在开始讲解DataFrame
的属性和方法前,我们先从之前提到的hrs
数据库中读取三张表的数据,创建出三个DataFrame
对象,代码如下所示。
import pymysql |
得到的三个DataFrame
对象如下所示。
部门表(dept_df
),其中dno
是部门的编号,dname
和dloc
分别是部门的名称和所在地。
dname dloc |
员工表(emp_df
),其中eno
是员工编号,ename
、job
、mgr
、sal
、comm
和dno
分别代表员工的姓名、职位、主管编号、月薪、补贴和部门编号。
ename job mgr sal comm dno |
说明:在数据库中
mgr
和comm
两个列的数据类型是int
,但是因为有缺失值(空值),读取到DataFrame
之后,列的数据类型变成了float
,因为我们通常会用float
类型的NaN
来表示空值。
员工表(emp2_df
),跟上面的员工表结构相同,但是保存了不同的员工数据。
ename job mgr sal comm dno |
DataFrame
对象的属性如下表所示。
属性名 | 说明 |
---|---|
at / iat |
通过标签获取DataFrame 中的单个值。 |
columns |
DataFrame 对象列的索引 |
dtypes |
DataFrame 对象每一列的数据类型 |
empty |
DataFrame 对象是否为空 |
loc / iloc |
通过标签获取DataFrame 中的一组值。 |
ndim |
DataFrame 对象的维度 |
shape |
DataFrame 对象的形状(行数和列数) |
size |
DataFrame 对象中元素的个数 |
values |
DataFrame 对象的数据对应的二维数组 |
关于DataFrame
的方法,首先需要了解的是info()
方法,它可以帮助我们了解DataFrame
的相关信息,如下所示。
代码:
emp_df.info() |
输出:
<class 'pandas.core.frame.DataFrame'> |
如果需要查看DataFrame
的头部或尾部的数据,可以使用head()
或tail()
方法,这两个方法的默认参数是5
,表示获取DataFrame
最前面5行或最后面5行的数据,如下所示。
emp_df.head() |
输出:
ename job mgr sal comm dno |
获取数据
索引和切片
如果要获取DataFrame
的某一列,例如取出上面emp_df
的ename
列,可以使用下面的两种方式。
emp_df.ename |
或者
emp_df['ename'] |
执行上面的代码可以发现,我们获得的是一个Series
对象。事实上,DataFrame
对象就是将多个Series
对象组合到一起的结果。
如果要获取DataFrame
的某一行,可以使用整数索引或我们设置的索引,例如取出员工编号为2056
的员工数据,代码如下所示。
emp_df.iloc[1] |
或者
emp_df.loc[2056] |
通过执行上面的代码我们发现,单独取DataFrame
的某一行或某一列得到的都是Series
对象。我们当然也可以通过花式索引来获取多个行或多个列的数据,花式索引的结果仍然是一个DataFrame
对象。
获取多个列:
emp_df[['ename', 'job']] |
获取多个行:
emp_df.loc[[2056, 7800, 3344]] |
如果要获取或修改DataFrame
对象某个单元格的数据,需要同时指定行和列的索引,例如要获取员工编号为2056
的员工的职位信息,代码如下所示。
emp_df['job'][2056] |
或者
emp_df.loc[2056]['job'] |
或者
emp_df.loc[2056, 'job'] |
我们推荐大家使用第三种做法,因为它只做了一次索引运算。如果要将该员工的职位修改为“架构师”,可以使用下面的代码。
emp_df.loc[2056, 'job'] = '架构师' |
当然,我们也可以通过切片操作来获取多行多列,相信大家一定已经想到了这一点。
emp_df.loc[2056:3344] |
输出:
ename job mgr sal comm dno |
数据筛选
上面我们提到了花式索引,相信大家已经联想到了布尔索引。跟ndarray
和Series
一样,我们可以通过布尔索引对DataFrame
对象进行数据筛选,例如我们要从emp_df
中筛选出月薪超过3500
的员工,代码如下所示。
emp_df[emp_df.sal > 3500] |
输出:
ename job mgr sal comm dno |
当然,我们也可以组合多个条件来进行数据筛选,例如从emp_df
中筛选出月薪超过3500
且部门编号为20
的员工,代码如下所示。
emp_df[(emp_df.sal > 3500) & (emp_df.dno == 20)] |
输出:
ename job mgr sal comm dno |
除了使用布尔索引,DataFrame
对象的query
方法也可以实现数据筛选,query
方法的参数是一个字符串,它代表了筛选数据使用的表达式,而且更符合 Python 程序员的使用习惯。下面我们使用query
方法将上面的效果重新实现一遍,代码如下所示。
emp_df.query('sal > 3500 and dno == 20') |
重塑数据
有的时候,我们做数据分析需要的原始数据可能并不是来自一个地方,就像上面的例子中,我们从关系型数据库中读取了三张表,得到了三个DataFrame
对象,但实际工作可能需要我们把他们的数据整合到一起。例如:emp_df
和emp2_df
其实都是员工的数据,而且数据结构完全一致,我们可以使用pandas
提供的concat
函数实现两个或多个DataFrame
的数据拼接,代码如下所示。
all_emp_df = pd.concat([emp_df, emp2_df]) |
输出:
ename job mgr sal comm dno |
上面的代码将两个代表员工数据的DataFrame
拼接到了一起,接下来我们使用merge
函数将员工表和部门表的数据合并到一张表中,代码如下所示。
先使用reset_index
方法重新设置all_emp_df
的索引,这样eno
不再是索引而是一个普通列,reset_index
方法的inplace
参数设置为True
表示,重置索引的操作直接在all_emp_df
上执行,而不是返回修改后的新对象。
all_emp_df.reset_index(inplace=True) |
通过merge
函数合并数据,当然,也可以调用DataFrame
对象的merge
方法来达到同样的效果。
pd.merge(dept_df, all_emp_df, how='inner', on='dno') |
输出:
dno dname dloc eno ename job mgr sal comm |
merge
函数的一个参数代表合并的左表、第二个参数代表合并的右表,有SQL编程经验的同学对这两个词是不是感觉到非常亲切。正如大家猜想的那样,DataFrame
对象的合并跟数据库中的表连接非常类似,所以上面代码中的how
代表了合并两张表的方式,有left
、right
、inner
、outer
四个选项;而on
则代表了基于哪个列实现表的合并,相当于 SQL 表连接中的连表条件,如果左右两表对应的列列名不同,可以用left_on
和right_on
参数取代on
参数分别进行指定。
如果对上面的代码稍作修改,将how
参数修改为left
,大家可以思考一下代码执行的结果。
pd.merge(dept_df, all_emp_df, how='left', on='dno') |
运行结果比之前的输出多出了如下所示的一行,这是因为left
代表左外连接,也就意味着左表dept_df
中的数据会被完整的查出来,但是在all_emp_df
中又没有编号为40
部门的员工,所以对应的位置都被填入了空值。
17 40 运维部 天津 NaN NaN NaN NaN NaN NaN |