import numpy as np
import pandas as pd
from pandas import Series,DataFrame
# import matplotlib.pyplot as plt
2、DataFrame
DataFrame是一个【表格型】的数据结构,可以看做是【由Series组成的字典】(多个series共用同一个索引)。DataFrame由按一定顺序排列的多列数据组成。设计初衷是将Series的使用场景从一维拓展到多维。DataFrame既有行索引,也有列索引。
- 行索引:index
- 列索引:columns
- 值:values(numpy的二维数组)
1)DataFrame的创建
最常用的方法是传递一个字典或二维数组来创建。
# DataFrame([1,2,3,4])
nd = np.random.randint(0,150,size=(5,4))
nd
index=['张三','李四','王老五','zhaoliu','田七']
columns = ['python','java','c','math']
# 成绩表
# 参数 data是数据 index是行索引 columns是列索引
df = DataFrame(data=nd,index=index,columns=columns)
df
DataFrame(data={
'A':[1,2,3,4],
'B':[1,2,3,4],
'C':[1,2,3,4],
})
另外通过导入csv文件得到的也是DataFrame
heights = pd.read_csv('./data/president_heights.csv')
heights
DataFrame属性:values、columns、index、shape
heights.values # 可以把DataFrame中的数据提取出来 提取出来的是多维数组
heights.columns # 可以获取列名
heights.index # 获取行索引
heights.shape
============================================
练习4:
根据以下考试成绩表,创建一个DataFrame,命名为df:
张三 李四
语文 150 0
数学 150 0
英语 150 0
理综 300 0
============================================
data = [[150,0],[150,0],[150,0],[300,0]]
data
index = ['语文','数学','英语','理综']
columns = ['张三','李四']
DataFrame(data=data,index=index,columns=columns)
2)DataFrame的索引
(1) 对列进行索引
- 通过类似字典的方式
- 通过属性的方式
可以将DataFrame的列获取为一个Series。返回的Series拥有原DataFrame相同的索引,且name属性也已经设置好了,就是相应的列名。
df
df['python'] # 获取列 可以通过中括号传入列名
pandas.core.series.Series
type(df['python'])
pandas.core.series.Series
df.python # 获取到的是 Series
张三 125
李四 67
王老五 14
zhaoliu 24
田七 140
Name: python, dtype: int32
(2) 对行进行索引
- 使用.loc[]加index来进行行索引
- 使用.iloc[]加整数来进行行索引
同样返回一个Series,index为原来的columns。
df
# df['zhaoliu']
# df.zhaoliu
# 行相当于一共一共的样本 列相当于是对象的各种特征
# 列更重要
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-31-b53296e268a4> in <module>()
1 # df['zhaoliu']
----> 2 df.zhaoliu
~\Anaconda3\lib\site-packages\pandas\core\generic.py in __getattr__(self, name)
4370 if self._info_axis._can_hold_identifiers_and_holds_name(name):
4371 return self[name]
-> 4372 return object.__getattribute__(self, name)
4373
4374 def __setattr__(self, name, value):
AttributeError: 'DataFrame' object has no attribute 'zhaoliu'
df.loc['zhaoliu'] # 获取到的也是Series
df.iloc[3]
python 24
java 147
c 85
math 128
Name: zhaoliu, dtype: int32
# 总结
# 对列进行索引 df['列名'] 或者 df.列名 获取到的是Series
# 对行进行索引 df.loc['行名'] 或者 df.iloc[索引号] 获取到的也是Series
(3) 对元素索引的方法
- 使用列索引
- 使用行索引(iloc[3,1]相当于两个参数;iloc[[3,3]] 里面的[3,3]看做一个参数)
- 使用values属性(二维numpy数组)
df
df.python
张三 125
李四 67
王老五 14
zhaoliu 24
田七 140
Name: python, dtype: int32
df['python']
df.python.loc['张三']
df.python.iloc[0]
125
df
df.loc['张三'].loc['python']
125
df
# DataFrame的loc和iloc中也可以使用 整数数组索引的形式
df.loc['张三','python'] # loc是 先行后列
125
df.iloc[0,0]
df
df.iloc[0,0]
df.iloc[[0,1]]
df.iloc[[0,0]]
# 总结
# df.loc 里面用字符串索引 df.loc['行名','列名']
# df.iloc 里面用编号 df.iloc[行索引,列索引]
# 韭菜 榴莲 臭豆腐 辣条
# 王总 刘总 班长 学委 也是委员
data = np.random.randint(0,100,size=(5,4))
i = ['王总','刘总','班长','学委','也是委员']
c = ['韭菜','榴莲','臭豆腐','大蒜']
df = DataFrame(data=data,index=i,columns=c)
df
【注意】
直接用中括号时:
- 索引表示的是列索引
- 切片表示的是行切片
df['韭菜'] # 索引是列索引
王总 92
刘总 77
班长 96
学委 0
也是委员 46
Name: 韭菜, dtype: int32
df['刘总':'学委'] # 切片是行切片
df[0:3] # 切片可以用 文字 也可以用数值
如果非要对列进行切片可以使用loc或iloc的整数数组索引的形式
df
df.loc['王总','韭菜']
df.loc['王总':'班长']
df.loc[:,'韭菜':'臭豆腐']
df.iloc[:,0:3]
============================================
练习5:
使用多种方法对df进行索引和切片,并比较其中的区别
获取 王总 刘总 的 所有爱好
获取 所有人的 韭菜 臭豆腐 的喜爱程度
============================================
df
df['王总':'刘总']
df.loc[['王总','刘总']]
df.loc[:,['韭菜','臭豆腐']]
df.values
array([[92, 6, 85, 8],
[77, 15, 0, 49],
[96, 63, 1, 37],
[ 0, 53, 83, 21],
[46, 91, 54, 10]])
df.values[2,2]
3)DataFrame的运算
(0) df和数值
df
df + 2
df * 2
df.iloc[3] = df.iloc[3] + 200
df.iloc[3] += 200
df
df.iloc[3,0] += 100
df
(1) DataFrame之间的运算
同Series一样:
- 在运算中自动对齐不同索引的数据
- 如果索引不对应,则补NaN
创建DataFrame df1 不同人员的各科目成绩,月考一
df1 = DataFrame(np.random.randint(0,150,size=(5,4)),index=['jack','rose','tom','jerry','bob'],columns=['python','math','english','chinese'])
df1
创建DataFrame df2 不同人员的各科目成绩,月考二
有新学生转入
df2 = DataFrame(np.random.randint(0,150,size=(5,4)),index=['jack','rose','tom','jerry','bob'],columns=['python','math','english','chinese'])
df2
df1+df2
下面是Python 操作符与pandas操作函数的对应表:
Python Operator | Pandas Method(s) |
---|---|
+ |
add() |
- |
sub() , subtract()
|
* |
mul() , multiply()
|
/ |
truediv() , div() , divide()
|
// |
floordiv() |
% |
mod() |
** |
pow() |
(2) Series与DataFrame之间的运算
【重要】
使用Python操作符:以行为单位操作(参数必须是行),对所有行都有效。(类似于numpy中二维数组与一维数组的运算,但可能出现NaN)
-
使用pandas操作函数:
axis=0:以列为单位操作(参数必须是列),对所有列都有效。 axis=1:以行为单位操作(参数必须是行),对所有行都有效。
df1 = DataFrame(data=np.random.randint(0,150,size=(5,5)),index=list('abcde'),columns=list('12345'))
df1
s1 = Series(data=np.random.randint(0,150,size=5),index=list('01234'))
s1
0 138
1 122
2 58
3 111
4 34
dtype: int32
s2 = Series(data=np.random.randint(0,150,size=5),index=list('abcde'))
s2
a 49
b 40
c 128
d 60
e 122
dtype: int32
df1 + s1 # df中的每一行都和Series去相加 是 对应项相加 如果没有对应项 用NaN补
df1 + s2
df1.add(s1)
# df1.add(s2,axis='columns') # 默认是每一行相加 让列名对应
df1.add(s2,axis='index')
============================================
练习6:
假设ddd是期中考试成绩,ddd2是期末考试成绩,请自由创建ddd2,并将其与ddd相加,求期中期末平均值。
假设张三期中考试数学被发现作弊,要记为0分,如何实现?
李四因为举报张三作弊立功,期中考试所有科目加100分,如何实现?
后来老师发现有一道题出错了,为了安抚学生情绪,给每位学生每个科目都加10分,如何实现?
============================================
# 以后就别删这个了
df1 = DataFrame(data=np.random.randint(0,150,size=(3,3)),index=['张三','李四','王五'],columns=['语文','数学','英语'])
df1
# 以后就别删这个了
df2 = DataFrame(data=np.random.randint(0,150,size=(3,3)),index=['张三','李四','王五'],columns=['语文','数学','英语'])
df2
(df1+df2)/2
df1.iloc[0,1] = 0
df1
df1.loc['李四'] += 100
df1
df1+=10
df1
df1>100
处理丢失数据
1.有两种丢失数据:
- None: Python自带的数据类型 不能参与到任何计算中
- np.nan(NaN): float类型 能参与计算,但结果总是NaN
np.NaN + 2
nan
2. np.nan(NaN)
数组直接运算会得到nan,但可以使用np.nan*()函数来计算nan,此时视nan为0。
nd = np.array([1,3,5,np.nan])
nd
nd.sum()
# nd.nansum() # ndarray对象是没有nan*()方法的
np.sum(nd)
np.nansum(nd)
Series和DataForm可以直接处理nan
3. pandas中的None与NaN
- pandas中None与np.nan都视作np.nan
用randint创建一个5*5的DataFrame作为例子
df = DataFrame(data=np.random.randint(0,10,size=(5,5)),columns=list('abcde'))
df
使用DataFrame行索引与列索引修改一下DataFrame数据(弄出来一些None和NaN)
s1 = Series([1,3,5,np.nan,None])
s1
s1.sum()
df1 = DataFrame([1,3,5,np.nan,None])
df1
df1.sum()
2) pandas中None与np.nan的操作-pandas%E4%B8%ADNone%E4%B8%8Enp.nan%E7%9A%84%E6%93%8D%E4%BD%9C)
isnull()
notnull()
-
dropna()
: 过滤丢失数据 -
fillna()
: 填充丢失数据
(1)判断函数
isnull()
notnull()
df
df.loc[1,'a'] = np.nan
df.loc[3,'b'] = np.nan
df.loc[0,'e'] = np.nan
df
df.isnull() # 是nan就是True 不是就是False
df.notnull()
# df.any() # 默认是axis=0 是 每一列
# df.any(axis=1)
df.isnull() # isnull() 有空值就是true
df.isnull().any() # any()只要有True就是True
# 所以 通过 any() 发现某一列是True 说明这一列有空值
df.isnull().any(axis=1) # 可以确定那一行(哪一个样本)里有空值
(2) 过滤函数
dropna()
可以选择过滤的是行还是列(默认为行)
df.dropna() # 直接干掉有nan的行
# {'any', 'all'}
# df.dropna(how='any') # 默认是any 就是只要这一行有nan就把这一行干掉
df.dropna(how='all') # 所有的都是NaN才把这一行干掉
df.iloc[0] = np.nan
df.dropna(how='all')
(3) 填充函数 Series/DataFrame
fillna()
# value=None 可以指定填充的值
df.fillna(value=0)
# method {'backfill', 'bfill', 'pad', 'ffill', None} 从后面找值来填充nan 从前面找值来填充nan
# df.fillna(method='bfill')
# df.fillna(method='ffill')
# axis=None 填充时沿着哪个轴线
df.fillna(method='bfill',axis=1)
df.fillna(method='bfill',limit=1) # limit 可以限制 连续填充nan的个数
# value=None, method=None, axis=None, inplace=False, limit=None,
# value 设定了 value 表示nan都用这个值来填充
# method 指定从前面填充还是从后面填充
# axis 指定填充内容的轴线 默认竖着的 axis=1就是横着
# inplace 是否替换原df
# limit 最多连续填充几个nan
df.fillna()
pandas的拼接操作
pandas的拼接分为两种:
- 级联:pd.concat, pd.append (没有重复数据)
- 合并:pd.merge, pd.join (有重复数据)
1. 使用pd.concat()级联
为方便讲解,我们首先定义两个DataFrame:
# 留着
df1 = DataFrame(nd1)
df1
# 留着
df2 = DataFrame(nd2)
df2
### 1) 简单级联[](http://localhost:8888/notebooks/00/03/c03_pandas_combine.ipynb#1)--%E7%AE%80%E5%8D%95%E7%BA%A7%E8%81%94)
pandas使用pd.concat函数,与np.concatenate函数类似
# objs 就是 要拼接的df
df3 = pd.concat((df1,df2))
df3
df3.loc[0] # 因为两个df的行名有重复的 所以 按照重复的行名 去检索 获得了两行
# ignore_index=False 忽略原来的索引 重新分配索引 默认是False
df3 = pd.concat((df1,df2),ignore_index=True)
df3
# axis=0 控制拼接的方向 默认是0 是 纵向拼接
df3 = pd.concat((df1,df2),axis=0)
df3 = pd.concat((df1,df2),axis=1)
df3 = pd.concat((df1,df2),axis=1,ignore_index=True)
df3
2) 不匹配级联-%E4%B8%8D%E5%8C%B9%E9%85%8D%E7%BA%A7%E8%81%94)
不匹配指的是级联的维度的索引不一致。例如纵向级联时列索引不一致,横向级联时行索引不一致
有3种连接方式:
外连接:补NaN(默认模式)
内连接:只连接匹配的项
指定连接轴 join_axes
# 留着
df3 = DataFrame(data=nd1,columns=list('abc'))
df4 = DataFrame(data=nd2,columns=list('bcd'))
display(df3,df4)
# pd.concat((df3,df4)) # 警告
pd.concat((df3,df4),sort=False) # 对不上的列 补NaN
# join='outer' join用来控制拼接方式 默认outer外联(有的都要) 还 可以 设置 inner (保留相同的列)
pd.concat((df3,df4),sort=False)
pd.concat((df3,df4),join='outer',sort=False)
pd.concat((df3,df4),join='inner',sort=False)
# 另外还可以自己制定拼接那些列
# join_axes=None 可以设定 按照那些列去拼接
pd.concat((df3,df4),join_axes=[df3.columns])
# 还可以自己制定列
index = pd.Index(['b','c'])
pd.concat((df3,df4),join_axes=[index])
# objs, axis=0, join='outer', join_axes=None, ignore_index=False
# objs 传入要拼接的 df
# axis 是 拼接的方向
# join 设定拼接的方式 有inner内联 和 outer外联
# join_axes 用来指定 按照哪些列去拼接
# ignore_index 忽略原来的索引 重新建立索引
pd.concat()
2. 使用pd.merge()合并
merge与concat的区别在于,merge需要依据某一共同的行或列来进行合并
注意每一列元素的顺序不要求一致
1) 一对一合并
2) 多对一合并
3) 多对多合并
t1 = pd.read_excel('./data/demo.xls',sheet_name=0)
t2 = pd.read_excel('./data/demo.xls',sheet_name=1)
t3 = pd.read_excel('./data/demo.xls',sheet_name=2)
t4 = pd.read_excel('./data/demo.xls',sheet_name=3)
t5 = pd.read_excel('./data/demo.xls',sheet_name=4)
案例练习:美国各州人口数据分析
知识补充
series.unique() 可以去重
首先导入文件,并查看数据样本
df_abbr = pd.read_csv('./data/state-abbrevs.csv')
df_abbr
df_areas = pd.read_csv('./data/state-areas.csv')
df_areas
df_popu = pd.read_csv('./data/state-population.csv')
df_popu
df_popu.tail()
df_popu.head()
合并popu与abbrevs两个DataFrame,分别依据state/region列和abbreviation列来合并。
为了保留所有信息,使用外合并。
df1 = pd.merge(df_popu,df_abbr,how='outer',left_on='state/region',right_on='abbreviation')
df1
df1.isnull().any() # isnull()有空值就是True
# any()这一列有True就是True
# 这一列有空值就是True
去除abbreviation的那一列(axis=1)
# axis默认是0 是干掉指定的行 axis=1是干掉指定的列
df2 = df1.drop(labels='abbreviation',axis=1) # drop()可以干掉指定的行或者列
df2
df2.isnull().any()
查看存在缺失数据的列。
使用.isnull().any(),只有某一列存在一个缺失数据,就会显示True。
df2.isnull().any(axis=1) # 获取到一系列的布尔值
# 把布尔值传入中括号 如果布尔值是True就会把对应位置的值取出来
df2[df2.isnull().any(axis=1)] # 获取有NaN的行
# df2[df2.isnull().any(axis=1)].unique() # unique是Series的方法 df不能用
df2[df2.isnull().any(axis=1)]['state/region'] # 按照索引把指定列取出 取出来的是Series
df2[df2.isnull().any(axis=1)]['state/region'].unique()
df2[df2.isnull().any(axis=1)]
为找到的这些state/region的state项补上正确的值,从而去除掉state这一列的所有NaN!
不能直接把值设置给DataFrame 要用DataFrame给DataFrame赋值
# 找state/region中是波多黎各的行 把 state 设置成 Puerto Rico
df2['state/region']=='PR'
# df2[df2['state/region']=='PR']['state'] = 'Puerto Rico'
temp = df2[df2['state/region']=='PR'].copy()
temp
temp['state'] = 'Puerto Rico'
temp
df2[df2['state/region']=='PR']= temp
df2.isnull().any()
# 找state/region中是波多黎各的行 把 state 设置成 Puerto Rico
df2['state/region']=='USA'
# df2[df2['state/region']=='PR']['state'] = 'Puerto Rico'
temp2 = df2[df2['state/region']=='USA'].copy()
temp2
temp2['state'] = 'United States'
temp2
df2[df2['state/region']=='USA'] = temp2
df2.isnull().any()
df3 = df2.dropna()
df3.isnull().any()
合并各州人口数据和面积数据areas,使用外合并。
df4 = pd.merge(df3,df_areas,how='outer')
df4
df4[df4.isnull().any(axis=1)]
df5 = df4.dropna()
df5.isnull().any()
找出2010年的全民人口数据,df.query(查询语句)
df5.query('year==2010&ages=="total"')
对查询结果进行处理,以state列作为新的行索引:set_index
df6 = df5.set_index('state')
df6.head()
计算人口密度。注意是Series/Series,其结果还是一个Series。
density = df6['population']/df6['area (sq. mi)']
density
排序,并找出人口密度最高的五个州sort_values()的密度
# sort_values是对值进行排序
density.sort_values().tail()
density.sort_values().head()