import numpy as npimport pandas as pdfrom 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属性也已经设置好了,就是相应的列名。
dfdf['python'] # 获取列 可以通过中括号传入列名pandas.core.series.Seriestype(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'] # 获取到的也是Seriesdf.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 + 2nan
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 不是就是Falsedf.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 最多连续填充几个nandf.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 就是 要拼接的dfdf3 = pd.concat((df1,df2)) df3
df3.loc[0] # 因为两个df的行名有重复的 所以 按照重复的行名 去检索 获得了两行
# ignore_index=False 忽略原来的索引 重新分配索引 默认是Falsedf3 = 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'] # 按照索引把指定列取出 取出来的是Seriesdf2[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 Ricodf2['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 Ricodf2['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()
作者:仙灵儿
链接:https://www.jianshu.com/p/a7523df7048e