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 |