手记

Pandas

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 OperatorPandas 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:

  1. 假设ddd是期中考试成绩,ddd2是期末考试成绩,请自由创建ddd2,并将其与ddd相加,求期中期末平均值。

  2. 假设张三期中考试数学被发现作弊,要记为0分,如何实现?

  3. 李四因为举报张三作弊立功,期中考试所有科目加100分,如何实现?

  4. 后来老师发现有一道题出错了,为了安抚学生情绪,给每位学生每个科目都加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

  1. 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


1人推荐
随时随地看视频
慕课网APP