Pandas基础和应用

pandas在numpy的基础上,优化了数据结构,在数据的存储、读取、切割、转换等方面进行了改进,使操作更简单。

常用数据对象

pandas提供三种数据对象,分别是Series(保存一维类数据),DataFrame(保存二维类数据,表格)和Panel(保存三维类或可变维度数据)

Series对象

解决数组索引含义不明确的问题

Series对象有别于Numpy的ndarray对象,它显示的内容不仅包含元素,也包含索引,并且这个索引可以是有意义的

如果索引不传值,那就是位置索引,0,1,2,......

data可以是数组、列表等类数组对象,也可以数字,字符串,也可以是字典

当data是字典时,标签索引由key来充任

#index参数用来指定顺序
gdp=pd.Series({"wuhan":5225.6,"nanjing":5558.6,'hangzhou':5255.4},
              index=['nanjing','hangzhou','wuhan'])
print(gdp)
"""
nanjing     5558.6
hangzhou    5255.4
wuhan       5225.6
dtype: float64
"""

#每一个series对象都有index和values两个基本属性,可以获得标签索引和值,也可以进行修改

"""
Series对象具有name属性,可以对本对象进行描述
标签索引也是对象——index对象,具有name属性,可以进行描述
"""
gdp.name="GDP DATA(bian de)"
gdp.index.name='city'
print(gdp)
"""
city
nanjing     5558.6
hangzhou    5255.4
wuhan       5225.6
Name: GDP DATA(bian de), dtype: float64
"""

DataFrame对象

使用二维数组存储表,就无法存储二维表的字段名称,所以引入DataFrame

pd.DataFrame()

主要3个参数,data,index(行索引标签),columns(列索引标签)

data可以是嵌套列表、二维数组、字典或者是DataFrame对象。字典的数据除字符串,数字等基本Python数据类型外,可以包含Series、数组等序列化类型的对象。

#data是嵌套列表
gp=pd.DataFrame([[2521.6,4222.5],[22552.5,5222.6],[53233.3,52522.6]],
                index=['Shanghai','nanjing','qidong'],
                columns=['GDP','population'])
#和series一样可以给columns和index命名
gp.index.name='City'
gp.columns.name='items'
print(gp)
"""
items         GDP  population
City                         
Shanghai   2521.6      4222.5
nanjing   22552.5      5222.6
qidong    53233.3     52522.6
"""

#data是字典,字典的key充当columns,字典的value充当具体的值
gdp=pd.DataFrame({'GDP':[2521.6,22552.5,53233.3],'Population':[4222.5,5222.6,52522.6]},
                 index=['Shanghai','nanjing','qidong'])

#data也可以是自定义类型的数组
data=np.array([('beijing',100.00),('hubei',82.57),('shanghai',82.47)],dtype=[("city",'S30'),('marks',np.float_)])
test=pd.DataFrame(data,index=['PKU','WHU','Fudan'])
print(test)
"""
              city   marks
PKU     b'beijing'  100.00
WHU       b'hubei'   82.57
Fudan  b'shanghai'   82.47
"""

 数据格式转换

使用pd.DataFrame.from_此类方法的作用:将某种格式的数据转换为DataFrame对象

比如:pd.DataFrame.from_dict(data,orient='columns',dtype=None)    把字典数据转换为DataFrame对象

参数orient默认为columns,意思是字典的key是列的标签索引,如果orient=‘index’,字典的key是行的标签索引

#创建的字典是{field:array_like}样式
dict_gdp={'GDP':[25522.5,52233.5,54555.3],'Population':[54423.1,55332.3,45656.7]}
print(pd.DataFrame.from_dict(dict_gdp))
"""
       GDP  Population       字典的key变为列的标签索引
0  25522.5     54423.1
1  52233.5     55332.3
2  54555.3     45656.7
"""

#创建的字典是{field:dict}样式
dict_gdp1={'GDP':{'Beijing':5222.2,'Shanghai':4522.6,'chengdu':5344.6},
          'Population':{'Beijing':52354.6,'Shanghai':45633.2,'chengdu':4555.6}}
print(pd.DataFrame.from_dict(dict_gdp1,orient='index'))  #字典的key变为行的标签索引
"""
            Beijing  Shanghai  chengdu
GDP          5222.2    4522.6   5344.6
Population  52354.6   45633.2   4555.6
"""

items_gdp=dict_gdp.items()
#dict_items([('GDP', [25522.5, 52233.5, 54555.3]), ('Population', [54423.1, 55332.3, 45656.7])])

逆过程:将DataFrame对象转换为其他类型的数据

dict_gdp1={'GDP':{'Beijing':5222.2,'Shanghai':4522.6,'chengdu':5344.6},
          'Population':{'Beijing':52354.6,'Shanghai':45633.2,'chengdu':4555.6}}
gp=pd.DataFrame.from_dict(dict_gdp1)
"""
             GDP  Population
Beijing   5222.2     52354.6
Shanghai  4522.6     45633.2
chengdu   5344.6      4555.6
"""

pd.DataFrame.to_csv(gp,'D:/gp.csv')

Panel对象

上面是二维方式下展示的三维数据

三个参数

items:即axis=0,对应表格China,USA

major_axis:即axis=1,对应表格Beijing,Hong Kong,New York,Chicage

minor_axis:即axis=2,对应表格Average Temparature,Altitude

最新版本,已将Panel这个类移除

对于三维数据,我们也可以使用DataFrame对象的多级索引MultiIndex对象

索引对象

Index对象

Index对象是序列,可以看成是一维数组,跟一维数组最大的区别是不可变。Index对象是pd.Index()的实例。

ind=pd.Index(['chinese','math','english','physics'])
print(pd.Series([100,98,97,95],index=ind)),
"""
chinese    100
math        98
english     97
physics     95
dtype: int64
"""

MultiIndex对象

Nation作为第0级索引,City作为第1级索引

创建MultiIndex对象

(1)通过pd.MultiIndex.from_函数创建

#tuples接收一个列表或者类数组的方式表示的多级索引
cities_index=[('China','Beijing'),('China','HongKong'),('USA','New York'),('USA','Chicago')]
cities=pd.MultiIndex.from_tuples(cities_index)
"""
MultiIndex([('China',  'Beijing'),
            ('China', 'HongKong'),
            (  'USA', 'New York'),
            (  'USA',  'Chicago')],
           )
"""

city_array=[['China','China','USA','USA'],['Beijing','HongKong','New York','Chicago']]
print(pd.MultiIndex.from_arrays(city_array))

#依据笛卡儿积计算MultiIndex对象
print(pd.MultiIndex.from_product([['a','b'],[100,200]]))
"""
MultiIndex([('a', 100),
            ('a', 200),
            ('b', 100),
            ('b', 200)],
           )
"""

(2)通过pd.MultiIndex()实例化类来创建对象

注意:两个参数levels:以序列类数据表示标签索引  codes:以整数的方式表示每个标签索引的位置

mulind=pd.MultiIndex(levels=[['China','USA'],['Bejing','hongkong','new york','chincage']],
                     codes=[[0,0,1,1],[0,1,2,3]])
"""
MultiIndex([('China',   'Bejing'),
            ('China', 'hongkong'),
            (  'USA', 'new york'),
            (  'USA', 'chincage')],
           )
"""


#通过实例化pd.Index()类创建MultiIndex对象
mulind1=pd.Index([('china','beijing'),('china','hongkong'),('USA','New York'),
                      ('USA','Chincage')])

使用MultiIndex对象

Series对象转为DataFrame类对象:通过unstack()方法

逆过程stack()方法

gdp=pd.Series({('shanghai',2015):5452,('shanghai',2016):5423,('beijing',2015):5213,
               ('beijing',2016):5665,('jiangsu',2015):5432,('jiangsu',2016):5789})
"""
shanghai  2015    5452
          2016    5423
beijing   2015    5213
          2016    5665
jiangsu   2015    5432
          2016    5789
dtype: int64
跟以往不同是字典的key,key作为Series对象的索引,它是由两个元素组成的元祖,pandas自动将两个元素处理为两级索引
"""

#pd.DataFrame()类创建多级索引,index通过嵌套列表表示多级索引

a=np.array([[20155,44212],[55234,42331],[11114,55233]
            ,[45522,42112],[42533,44755],[75542,45963]])
gbp1=pd.DataFrame(a,index=[['shanghai','shanghai','beijing','beijing','jiangsu','jiangsu'],
                          [2015,2016,2015,2016,2015,2016]],
                 columns=['gdp','population'])
"""
                 gdp  population
shanghai 2015  20155       44212
         2016  55234       42331
beijing  2015  11114       55233
         2016  45522       42112
jiangsu  2015  42533       44755
         2016  75542       45963
"""

#不用嵌套列表,直接传入MultiIndex对象
gh_mind=pd.MultiIndex.from_arrays([['shanghai','shanghai','beijing','beijing','jiangsu','jiangsu'],
                          [2015,2016,2015,2016,2015,2016]])
gdp2=pd.DataFrame(a,index=gh_mind,columns=['GDP','Population'])
print(gdp2)

数据索引和切片

Series对象

Series对象是类字典的对象,那么可以使用字典的一些方法进行操作

有几个注意点

①Series对象对于标签索引的切片是前包含后也包括,但数组切片是前包括后不包括

②切片切出来的是一个新的Series对象,与原来的不共用同一个视图,但数组的切片是公用同一视图

③Series对象拥有标签索引的同时,位置索引也没有失效,还是前包括,后不包括

g=np.array([1523.2,442.2,4426.6,4555.7,47563.7,789.8])
gdp=pd.Series(g,index=['shanghai','beijing','chengdu','tianjin','zhejiang','jiangsu'])
"""
shanghai     1523.2
beijing       442.2
chengdu      4426.6
tianjin      4555.7
zhejiang    47563.7
jiangsu       789.8
dtype: float64
"""
gdp['shanghai']  #1523.2    Series对象是类字典对象

print(gdp[['shanghai','zhejiang','jiangsu']])  #还是返回一个新的Series对象,该对象与原来不共用一个视图
"""
shanghai     1523.2
zhejiang    47563.7
jiangsu       789.8
dtype: float64
"""
print(gdp['beijing':'tianjin'])  #标签索引,前包括后也包括
"""
beijing     442.2
chengdu    4426.6
tianjin    4555.7
dtype: float64
"""

print(gdp[2])  #4426.6  位置索引并没有失效

为了避免标签索引和位置索引都是整数带来的混乱,采用iloc和loc

series.iloc[]只接收位置索引          series.loc[]只接收标签索引

使用在MultiIndex对象

gdp=pd.Series({('shanghai',2015):5452,('shanghai',2016):5423,('beijing',2015):5213,
               ('beijing',2016):5665,('jiangsu',2015):5432,('jiangsu',2016):5789})
"""
shanghai  2015    5452
          2016    5423
beijing   2015    5213
          2016    5665
jiangsu   2015    5432
          2016    5789
dtype: int64
"""

gdp.loc['shanghai']
"""
2015    5452
2016    5423
dtype: int64
"""

gdp.iloc[0]  # 5452 注意,返回的是第一个数值

#取2015年的数据
#print(gdp.iloc[2015])  #出错
print(gdp[:,2015])
"""
shanghai    5452
beijing     5213
jiangsu     5432
dtype: int64
"""

DataFrame对象

类比二维数组的索引和切片

iloc()和loc不仅适用于Series对象,也适用于DataFrame对象

#建立一个典型的DataFrame对象,它的行和列都是以MultiIndex对象为索引
mind=pd.MultiIndex.from_product([[2020,2021],[1,2]],names=['year','term'])
columns=pd.MultiIndex.from_product([['zhangsan','lisi','wangwu'],['chinese','math']],
                                   names=['name','subject'])
#开始生成数据
data=np.round(np.random.randn(4,6),1)  #生成4行6列的二维数组,符合标准的正态分布
data=data*10+70
scores=pd.DataFrame(data,index=mind,columns=columns)
"""
name      zhangsan          lisi        wangwu      
subject    chinese  math chinese  math chinese  math
year term                                           
2020 1        68.0  65.0    74.0  68.0    56.0  64.0
     2        62.0  80.0    76.0  68.0    74.0  80.0
2021 1        75.0  67.0    88.0  57.0    81.0  70.0
     2        55.0  85.0    67.0  61.0    64.0  72.0
"""

#取李四的数学成绩
print(scores['lisi','math'])

#使用切片,取李四的数学成绩
print(scores.loc[:,('lisi','math')])


#取两年中第一学期所有人的数学成绩  不会

 文件读写操作

CSV文件

以纯文本形式保存表格数据(数字和文本)

读取csv文件:pd.read_csv()

参数解释

header:默认=0表示第一行作为列标签

names: 指定列标签内容

skiprows:忽略的行数 

index_col:指定某一列或者多列作为索引

gdp=pd.read_csv('gdp-population.csv',names=['city','GDP','Pop'],skiprows=[0],index_col=0)

对于有缺失的数据,pandas会自动处理,以NaN表示

na_values 用于替换NA的值

nrows指定需要读取的行数

注:在Python中,None是一个对象,而NaN是缺失数据的标记

chunksize指定文件块的大小,返回一个TextFileReader对象 

api_chunk=pd.read_csv('aqi.csv',chunksize=100,encoding='utf-8')  #一次读100个记录,300多个数据共4次
tot=pd.Series([])# Series.add()方法,其中fill_value参数表示在添加前要在列表中用NaN替换的值
#city['级别']返回的是series对象,series.value_counts()计算不同值在该列中有多少数据,返回series对象
for city in api_chunk:
    tot=tot.add(city['级别'].value_counts(),fill_value=0)
"""
中度污染      8.0
优       105.0
良       180.0
轻度污染     62.0
dtype: float64
"""

HDF5文件

HDF是指一种为存储和处理大容量科学数据设计的文件格式及相应库文件。当前流行版本是HDF5

HDF5文件包含两种基本数据对象。

群组(group):类似文件夹,可以包含多个数据集或下级群组

数据集(dataset):数据内容

f=h5py.File('test.hdf5','w')  #创建一个hdf5文件
d=f.create_dataset(name='/dataset1',shape=(99,99))
g=f.create_group('group1')
#在group中增加dataset d2=g.create_dataset(name='in_group1',shape=(20,)) d2.name #/group1/in_group1 #HDF5文件也是类字典对象,包括f.values() f.items() f.keys() 前面说到Series对象也可以看成类字典对象 d3=f['group1/in_group1'] # <HDF5 dataset "in_group1": shape (20,), type "<f4">

使用Pandas对此类文件进行读写

store=pd.HDFStore('data.h5')  #通过HDFStore()类创建HDF5对象,安装tables包
gdp=pd.read_csv('gdp-population.csv',names=['city','GDP','Pop'],skiprows=[0],index_col=0) #读取csv数据

store['gdp']=gdp['GDP']
store['gdp_pop']=gdp

print(store)
print(store['gdp_pop'])

处理缺失数据

numpy中的缺失数据

a=np.array([1,2,3,None,5])
print(a.dtype)  #判断其数组元素类型为object。也不能对数组a进行运算,会报错
b=np.array([1,2,3,np.nan,5])
print(b.dtype)  #float64   np.nan是浮点数,这个浮点数用来标记缺失数据
print(0*np.nan)  #nan,注意0*任意数不一定为0了
print(np.nansum(b)) # 11.0  numpy提供专门处理nan数据的函数

pandas处理缺失数据

s=pd.Series([1,np.nan,4,8,None,10])
"""
0     1.0
1     NaN
2     4.0
3     8.0
4     NaN
5    10.0
dtype: float64
"""
s.isnull()  #类似的s.notnull()
"""
0    False
1     True
2    False
3    False
4     True
5    False
dtype: bool
"""

s[s.notnull()]     #用返回的对象作为下标,对原有对象进行筛选
"""
0     1.0
2     4.0
3     8.0
5    10.0
dtype: float64
"""

#dropna()删除所有NaN数据,对于Series对象容易,对于DataFrame呢
df=pd.DataFrame([[1,2,np.nan,4],[5,None,7,8],[9,10,11,12]])
"""
参数讲解
axis默认=0,即删除行,如果axis=1,删除列
how默认=any,即删除有NaN的行或者列  how=all表示丢弃全为NaN的那些行或列
subset  指定删除哪几列的NaN
"""
df.dropna()
"""
   0     1     2   3
2  9  10.0  11.0  12
"""

#fillna() 用某个值将数据NaN替换掉
df.fillna(-1)  #与原来df不共用同一视图
"""
  0     1     2   3
0  1   2.0  -1.0   4
1  5  -1.0   7.0   8
2  9  10.0  11.0  12
"""

print(s.fillna(method='ffill'))  #用前面的数据进行替换  method='bfill'表示用后面的数据进行替换
"""
0     1.0
1     1.0
2     4.0
3     8.0
4     8.0
5    10.0
"""

#对于DataFrame对象要指定哪个轴进行前后对换
print(df.fillna(method='bfill',axis=1))
"""
     0     1     2     3
0  1.0   2.0   4.0   4.0
1  5.0   7.0   7.0   8.0
2  9.0  10.0  11.0  12.0
"""

规整数据

前面学习过规整数据的方法(数组的组合、分割操作),下面介绍pandas的一些方法

轴向连接

在numpy中,np.concatenate()通过axis参数实现沿所设置的轴方向连接数组。pandas提供类似的函数pd.concat()

参数介绍

objs:连接对象,必须

axis:默认为0,就是沿着0轴连接

keys:设置多级索引

join:join默认=‘outer’,取索引的并集连接  ,join='inner'表示取索引的交集

ignore_index:默认为False,如果=True,表示忽略原有索引,采用位置索引

s1=pd.Series([100,200,300],index=['a','b','c'])
df1=pd.DataFrame([[110,120,130],[210,220,230],[310,320,330]])

#Series对象与DataFrame连接,自动补充NaN,并且做了类型转换

pd.concat([s1,df1])
"""
     0      1      2
a  100    NaN    NaN
b  200    NaN    NaN
c  300    NaN    NaN
0  110  120.0  130.0               #索引也是顺序连接的
1  210  220.0  230.0
2  310  320.0  330.0
"""
df1=pd.DataFrame([[110,120,130],[210,220,230],[310,320,330]])
df1.index=['a','c','d']
df2=pd.DataFrame([[11,12,13],[21,22,23],[31,32,33]])
df2.index=['b','c','d']

pd.concat([df1,df2],axis=1)  #取并集join='outer',缺失数据自动补NaN
"""
       0      1      2     0     1     2
a  110.0  120.0  130.0   NaN   NaN   NaN
c  210.0  220.0  230.0  21.0  22.0  23.0
d  310.0  320.0  330.0  31.0  32.0  33.0
b    NaN    NaN    NaN  11.0  12.0  13.0
"""

pd.concat([df1,df2],axis=1,join='inner') #取交集
"""
     0    1    2   0   1   2
c  210  220  230  21  22  23
d  310  320  330  31  32  33
"""

#设置列的多级索引
df1.columns=['one','two','three']
df2.columns=['one','two','three']
pd.concat([df1,df2],axis=1,keys=['level1','level2'],names=['LEVEL','ROW'])
pd.concat({'level1':df1,'level2':df2},names=['LEVEL',"ROW"],axis=1)  #另外一种方式
"""
LEVEL level1               level2            
ROW      one    two  three    one   two three
a      110.0  120.0  130.0    NaN   NaN   NaN
c      210.0  220.0  230.0   21.0  22.0  23.0
d      310.0  320.0  330.0   31.0  32.0  33.0
b        NaN    NaN    NaN   11.0  12.0  13.0
"""

#除了pd.concat()完成轴向连接, 还有append(),但该方法不能实现在axis=1轴上的连接

print(df1.append(df2,ignore_index=True)) # 和列表的append()的区别:这个是新生成DataFrame对象,df1的值没有变;列表中的append()是对其原地修改
"""
   one  two  three
0  110  120    130
1  210  220    230
2  310  320    330
3   11   12     13
4   21   22     23
5   31   32     33
"""

合并数据

注意pd.merge()和pd.concat()区别

pd.merge()是两个对象取并集,pd.concat()是沿着某一轴将两者连接

pd.merge()是针对DataFrame而言,参数解释如下

how:默认为inner,取两个对象指定键的交集。how='outer'取并集   how='left/right'

on:指定要合并的键

left_on/right_on:从对象中选择列标签作为数据合并的键

left_index/right_index:默认为False,如果为True,则以left/right所引用对象的索引为键合并数据

indicator:默认为False,如果为True,增加_merge 列,注明每行数据的来源

除pd.merge()实现合并操作外,DataFrame实例对象的join()操作也可以

#读取数据
pop=pd.read_csv('state-population.csv')
areas=pd.read_csv('state-areas.csv')
abbrevs=pd.read_csv('state-abbrevs.csv')

#将pop和abbrevs合并,使得每一个pop后面有国家全称
pop_merged=pd.merge(pop,abbrevs,how='left',left_on='state/region',right_on='abbreviation')
#删掉重复的列
pop_merged=pop_merged.drop('abbreviation',axis=1)

#检查数据是否有空的情况
pop_merged.isnull().any()
"""
state/region    False
ages            False
year            False
population       True
state            True
dtype: bool
"""

#看一下population记录为空的数据
pop_merged[pop_merged['population'].isnull()].head()
"""
     state/region     ages  year  population state
2448           PR  under18  1990         NaN   NaN
2449           PR    total  1990         NaN   NaN
2450           PR    total  1991         NaN   NaN
2451           PR  under18  1991         NaN   NaN
2452           PR    total  1993         NaN   NaN
"""

#寻找所有state为NaN的state/region的值
pop_merged.loc[pop_merged['state'].isnull(),'state/region'].unique()
# ['PR' 'USA']
#说明PR和USA没有全称
pop_merged.loc[pop_merged['state/region']=='PR','state']='Puerto Rico'
pop_merged.loc[pop_merged['state/region']=='USA','state']='United States'
pop_merged.isnull().any()
"""
state/region    False
ages            False
year            False
population       True
state           False
dtype: bool
"""

#将areas也加入其中,共有的列
result=pd.merge(pop_merged,areas,on='state',how='outer')
result.isnull().any()  #发现area也有数据缺失

#看一下缺失的是哪些国家的面积
print(result.loc[result['area (sq. mi)'].isnull(),'state'].unique())
#['United States']

result.dropna(inplace=True)  #inplace=True实现原地修改,就是不创建中间变量,将删完的数据重新分配给原始变量
result.isnull().any() #现在没有为空的记录了

 组合数据

根据某个条件分别从不同的对象中选择数据,组成一个新的数据对象。

#Series对象
a=pd.Series([np.nan,2,4,np.nan,8,10])
b=pd.Series([1,2,np.nan,np.nan,5,np.nan])
b.combine_first(a)   #b中缺失的数据由a来补充
"""
0     1.0
1     2.0
2     4.0
3     NaN
4     5.0
5    10.0
dtype: float64
"""

#DataFrame类
df1=pd.DataFrame({'one':[11,12,np.nan,13,np.nan],'two':[np.nan,22,23,np.nan,24]})
df2=pd.DataFrame({'one':np.arange(6),'two':np.linspace(10,100,6),'three':np.logspace(2,3,6)})
print(df2.combine_first(df1))  #df1缺失的数据由df2补充,包括把缺失的行、列从df2取出来补充
"""
   one        three    two
0  0.0   100.000000   10.0
1  1.0   158.489319   28.0
2  2.0   251.188643   46.0
3  3.0   398.107171   64.0
4  4.0   630.957344   82.0
5  5.0  1000.000000  100.0
"""

数据转换

行列转换

data=pd.DataFrame(np.arange(10).reshape(2,5),index=['one','two'],columns=['a','b','c','d','e'])
data['e']=np.nan
print(data.stack(dropna=False))
#列转为行,DataFrame对象转换为Series对象,默认dropna=True,即转换结果中没有NaN
"""
one  a    0.0
     b    1.0              
     c    2.0
     d    3.0
     e    NaN
two  a    5.0                使用MultiIndex的Series对象
     b    6.0
     c    7.0
     d    8.0
     e    NaN
dtype: float64
"""

print(data.stack().unstack())  #再由行转换成列
"""
       a    b    c    d
one  0.0  1.0  2.0  3.0
two  5.0  6.0  7.0  8.0
"""

透视表

pivot()和pivot_table()区别:

pivot:无法聚合,只能重塑,如果存在重复数据将会报错;pivot_table()可以聚合,弥补了pivot的缺陷

students=pd.DataFrame({'class':['class1','class2','class1','class2'],
                       'subject':['physics','python','math','physics'],
                       'numbers':[28,30,20,80]})

#根据class数据再归类
students.pivot(index='class',columns='subject',values='numbers')
"""
subject  math  physics  python
class                         
class1   20.0     28.0     NaN
class2    NaN     80.0    30.0
"""

students2=pd.DataFrame({'class':['class1','class2','class1','class2','class2'],
                       'subject':['physics','python','math','physics','python'],
                       'numbers':[28,30,20,80,99]})

#存在一个条件有两个数据的情况,所以不能使用pivot(),会报错
print(students2.pivot_table(index='class',columns='subject',values='numbers',aggfunc='sum'))
"""
subject  math  physics  python
class                         
class1   20.0     28.0     NaN
class2    NaN     80.0   129.0
"""

综合运用

cnpop=pd.read_csv('cnpop.csv')

#增加一个列标签,标记每行记录所处的年代
cnpop['decade']=cnpop['year']//10*10

#统计每一个年代出生率,死亡率,增长率的平均值
print(cnpop.pivot_table(index='decade',values=['birth_rate','death_rate','growth_rate'],aggfunc='mean'))

统计运算

gdp=pd.read_csv('gdp-population.csv')
gdp=gdp.set_index('City_Name')
gdp.mean()  #pandas的实例方法,不是pandas的类方法
#默认沿着0轴方向对各列数据进行统计,参数skipna默认=True,即对于缺失的数据不计入统计范畴
"""
GDP           19319.79875
Population     1828.25125
dtype: float64
"""
#此对象的基本统计项目
print(gdp.describe())
"""
                GDP   Population
count      8.000000     8.000000
mean   19319.798750  1828.251250
std     4908.677545   645.654074
min    12170.200000  1137.870000
25%    17037.842500  1368.777500
50%    18688.995000  1576.940000
75%    20933.000000  2234.600000
max    27466.150000  3016.550000
"""

 分组运算

pandas分组(groupby)的运算过程:①split(分割) 得到分组对象 ②apply(应用) 应用分组对象的某个方法进行计算  ③combine(合并):对各组的计算结果合并

分组的键

参数by用来指明分组的键

df=pd.DataFrame({'subject':['math','physics','english','math','physics','english'],
                 'score':[90,80,70,95,85,75],
                 'teacher':['Netwon','Netwon','Pascal','Netwon','Pascal','Pascal'],
                 'rank':[4,5,2,9,7,5]})

df.groupby('subject').mean()  #指定一列为键   数据为字符串的列不参与运算
"""
         score  rank
subject             
english   72.5   3.5
math      92.5   6.5
physics   82.5   6.0
"""

df.groupby(['subject','teacher']).mean()  #指定多列,返回具有多级索引的DataFrame对象
"""
                 score  rank
subject teacher             
english Pascal    72.5   3.5
math    Netwon    92.5   6.5
physics Netwon    80.0   5.0
        Pascal    85.0   7.0
"""

#分组的键可以不是数据的本身
data=pd.DataFrame(np.random.randn(6,6),columns=['a','b','c','d','e','f'],
                  index=['first','second','third','forth','fifth','sixth'])
lst=['one','one','one','two','two','two']   #分组的键,lst列表长度和data列标签个数一样
#lst元素和data。columns建立了一对一的对应关系,分组的键可以是上面的列表形式,也可以是字典,Series,主要是建立起映射关系
data.groupby(lst,axis=1).mean()
"""
             one       two
first  -0.498949 -0.306100
second -0.013481 -0.886954
third   0.409185 -0.725453
forth   0.707199  0.627845
fifth   0.098086  0.672246
sixth   0.373017 -0.175056
"""

#分组的键可以是函数,自定义函数和Python内置函数
def is_t(name):
    if "t" in name:
        return True
    else:
        return False
data.groupby(is_t).mean()    #注意,传递的是函数对象
"""
              a         b         c         d         e         f
False  0.821196  0.879802 -0.716091  0.223925  0.748068 -0.015562
True   0.440988  0.148169  0.190818  0.145825  0.404146  0.265538
"""

data.groupby(len).mean()   #内置函数
"""
          a         b         c         d         e         f
5 -0.536603  0.064252 -0.691745 -0.166796  1.141152 -0.172374
6 -0.299885 -0.376578 -1.194604 -2.959855  1.103298 -0.398169
"""

分组对象的运算方法

下面介绍的都是分组对象的方法,他们都是以分组对象为单位进行计算

df=pd.DataFrame({'subject':['math','physics','english','math','physics','english'],
                 'score':[90,80,70,95,85,75],
                 'teacher':['Netwon','Netwon','Pascal','Netwon','Pascal','Pascal'],
                 'rank':[4,5,2,9,7,5]})


#1.aggregate():将若干个用于统计运算的函数聚合在一起
df.groupby('teacher').aggregate(['mean','std','max'])
"""
       score                    rank              
              mean       std max      mean       std max
teacher                                                 
Netwon   88.333333  7.637626  95  6.000000  2.645751   9
Pascal   76.666667  7.637626  85  4.666667  2.516611   7
"""

#对不同的列进行不同的运算,以字典形式建立列标签和函数的映射关系
df.groupby('teacher').aggregate({'score':['mean','max','min'],'rank':'std'})
"""
           score              rank
              mean max min       std
teacher                             
Netwon   88.333333  95  80  2.645751
Pascal   76.666667  85  70  2.516611
"""

#2.filter()  以分组对象为单位,若符合条件,将分组数据全部返回,不是逐条进行筛选
df.groupby('subject').filter(lambda x:x['rank'].mean()>5)  #英语分组的平均值<5,所以不返回
"""
   subject  score teacher  rank
0     math     90  Netwon     4
1  physics     80  Netwon     5
3     math     95  Netwon     9
4  physics     85  Pascal     7
"""


#3.transform() 改变原数据的值,返回值的长度和原数据相同,注意:以分组对象为单位
df.groupby('subject').transform(lambda x:x-x.mean())
"""
   score  rank
0   -2.5  -2.5
1   -2.5  -1.0
2   -2.5  -1.5
3    2.5   2.5
4    2.5   1.0
5    2.5   1.5
"""

#4.apply()
def rank_score(x):
    x['rank_score']=x['score']*x['rank'].std()  #乘的是每组rank的标准差
    return x
print(df.groupby('subject').apply(rank_score))  #传入函数对象

 pd.cut()方法返回category类型的对象——已经“装箱’’(划分了“类别”’)的对象

x=pd.Series([20,25,30,35,40])
bins=[10,30,40]
print(pd.cut(x,bins))
"""
0    (10, 30]
1    (10, 30]
2    (10, 30]
3    (30, 40]
4    (30, 40]
dtype: category
Categories (2, interval[int64]): [(10, 30] < (30, 40]]
"""

矢量化字符串

names=['newton','hertz',None,'curie']
snames=pd.Series(names)  #snames是矢量化字符串后的Series对象,和字符串有相同的方法,但功能要更强悍
snames.str.capitalize()  #首字母大写
"""
0    Newton
1     Hertz
2      None
3     Curie
dtype: object
"""

#包括进行正则表达式匹配,切片
snames.str[2]  #snames.str.get(2)使用此方法同等作用
"""
0       w
1       r
2    None
3       r
dtype: object
"""

snames.str[:4]  #使用snames.str.slice(0,4)同等作用
"""
0    newt
1    hert
2    None
3    curi
dtype: object
"""

与时间相关操作

时刻

python中提供datetime标准库来描述时刻,

pandas定义了名为Timestamp类型的对象描述时刻,并且Timestamp继承datetime

now=datetime.datetime.now()
now  #2021-05-20 20:50:26.034685
now.year  #2021
----------------------------------------------
now=pd.Timestamp.now()
print(now)  #2021-05-20 20:51:21.979260
print(now.dayofyear)#一年中的第几天
print(now.dayofweek)#本周中的第几天,周一为第0天
print(now.hour) #今天中的第几个小时

#通过以下实例化方式均可获得Timestamp对象
print(pd.Timestamp('1999.07.21'))
print(pd.Timestamp(1999,7,21))
print(pd.Timestamp(datetime.datetime(1999,7,21)))

时间间隔

pandas使用Timedelta来描述时间间隔

start=pd.Timestamp('1999.07.21')
now=pd.Timestamp.now()
delta=now-start
# 7974 days 21:04:10.931264    我已经生活了这么多天了

#一个时刻加上一个时间间隔,可以得到另外一个时刻
print(start+delta)  #2021-05-20 21:06:14.506062

#通过以下实例化方式可获得Timedelta类型对象
print(pd.Timedelta(days=5,hours=6,minutes=7,seconds=8))#5 days 06:07:08
print(pd.Timedelta(seconds=123456567)) #1428 days 21:29:27

周期

pandas描述周期的对象是Period

重要参数freq,来确定周期的频率

#实例化Period对象
now_week=pd.Period.now(freq='W')  #2021-05-17/2021-05-23

print(pd.Period('1999-07'))  #1999-07
print(pd.Period('1999-07',freq='D')) #1999-07-01

#我们可以通过Timestamp来创建Period对象
t1=pd.Timestamp('1999.07.21')
print(t1.to_period('W'))  #1999-07-19/1999-07-25

 比较相关模块

#datetime是python的标准库
datetime.datetime(year=1999,month=7,day=21)  #1999-07-21 00:00:00

#dateutil是以datetime为基础,提供扩展功能,比如解析字符串时间
from dateutil import parser
parser.parse("1999.07.21")  #1999-07-21 00:00:00
#以上都是针对单个数据操作

#数据分析基础模块numpy,提供一种针对日期/时间的专有数据类型datetime64

m=np.datetime64('1999-07-21')
m+np.arange(5)  #体现出以datetime64为数据类型创建的数据的优势
#['1999-07-21' '1999-07-22' '1999-07-23' '1999-07-24' '1999-07-25']

#更加方便的是,pandas的Timestamp
date_pd=pd.to_datetime('1999-07-21')
print(date_pd)  #1999-07-21 00:00:00
print(date_pd.strftime("%A"))  #%A表示格式符,星期的全拼  Wednesday
print(date_pd+pd.to_timedelta(np.arange(10),'D'))  #'D'表示指定单位为日
"""
DatetimeIndex(['1999-07-21', '1999-07-22', '1999-07-23', '1999-07-24',
               '1999-07-25', '1999-07-26', '1999-07-27', '1999-07-28',
               '1999-07-29', '1999-07-30'],
              dtype='datetime64[ns]', freq=None)
"""

时间索引

#pd.to_datetime()  如果传入一个时间对象,获得的是Timestamp对象,如果传入的是序列,则生成的是DatetimeIndex的索引对象
#同理,Period对象组成的序列就是PeriodIndex类型的索引对象
#也提供了将DatetimeIndex索引对象转换成PeriodIndex索引对象的方法

data_index=pd.to_datetime(['1999.07.21','1999.08.09','2002.06.08','2021.05.22'])
#DatetimeIndex(['1999-07-21', '1999-08-09', '2002-06-08', '2021-05-22'], dtype='datetime64[ns]', freq=None)

period_index=data_index.to_period('M') #参数以freq声明周期的单位
#PeriodIndex(['1999-07', '1999-08', '2002-06', '2021-05'], dtype='period[M]', freq='M')

#Timedelta对应的索引对象是TimedeltaIndex
delta_index=data_index-data_index[0]
#TimedeltaIndex(['0 days', '19 days', '1053 days', '7976 days'], dtype='timedelta64[ns]', freq=None)

使用pd.date_range(),pd.period_range(),pd.timedelta_range()生成三种元素对象,且元素的物理量按一定规律排列

pd.date_range('1999-07','1999-09',freq='M') #freq默认为'D'
#DatetimeIndex(['1999-07-31', '1999-08-31'], dtype='datetime64[ns]', freq='M')

#参数periods表示生成的数量    freq可以取的值很多,用的时候去查
print(pd.timedelta_range(0,periods=6,freq='3H30T'))  #3个半小时
"""
TimedeltaIndex(['0 days 00:00:00', '0 days 03:30:00', '0 days 07:00:00',
                '0 days 10:30:00', '0 days 14:00:00', '0 days 17:30:00'],
               dtype='timedelta64[ns]', freq='210T')
"""

重采样

重采样的对象必须是DatetimeIndex,PeriodIndex或TimedeltaIndex为索引,重采样就是变更这些索引的频率

d_index=pd.date_range('2019.07.21',periods=50,freq='D')
sdata=pd.Series(np.random.randn(len(d_index)),index=d_index)
"""
2019-07-21   -0.287214
2019-07-22    0.564339
2019-07-23   -1.206455
......
"""
#频率从日改为月,频率降低,也叫降采样
print(sdata.resample('M',kind='period').mean())  #kind参数表示聚合的类型
"""
2019-07   -0.063128
2019-08   -0.225223
2019-09   -0.168393
Freq: M, dtype: float64
"""

#直接使用下标也可以实现采用,sdate频率为日,下标为月,返回该月的全部数据
print(sdata['2019-09'])
"""
2019-09-01   -1.806521
2019-09-02   -1.584223
2019-09-03    1.062668
2019-09-04    0.051151
2019-09-05   -0.653908
2019-09-06   -0.466127
2019-09-07    1.335732
2019-09-08   -0.770979
"""

 

posted @ 2021-05-17 09:14  我的愿望是如你所愿  阅读(203)  评论(0)    收藏  举报