机器学习入门笔记二 pandas高级操作

这篇主要介绍一些我觉得常用的一些高级用法,主要包括groupby操作,apply,map操作,pivot_table操作,时间序列操作和字符串操作。

groupby

核心:

  1. 不论分组键是数组、列表、字典、Series、函数,只要其与待分组变量的轴长度一致都可以传入groupby进行分组。
  2. 默认axis=0按行分组,可指定axis=1对列分组。

对数据进行分组操作的过程可以概括为:split-apply-combine三步:

  1. 按照键值(key)或者分组变量将数据分组。
  2. 对于每组应用我们的函数,这一步非常灵活,可以是python自带函数,可以是我们自己编写的函数。
  3. 将函数计算后的结果聚合。

如下图:


image.png
#导入pandas库
import pandas as pd
#生成一个dataframe
data = pd.DataFrame({'key':['A','B','C','A','B','C','A','B','C'],
                     'value':[0,5,10,5,10,15,10,15,20]})
print(data)
print(data.groupby('key').sum())
  key  value
0   A      0
1   B      5
2   C     10
3   A      5
4   B     10
5   C     15
6   A     10
7   B     15
8   C     20
     value
key       
A       15
B       30
C       45

上面只是进行了sum操作,当然也可以使用许多的统计方法size,count,max,min,mean等等。

需要注意的地方就是size方法和count方法的区别,size是直接统计键的出现次数,所以结果里面是不会有value列的,而且size方法会统计缺失值,count不会。

import numpy as np
#导入numpy,设置一个值为nan值
data2 = pd.DataFrame({'key':['A','B','C','A','B'],'value':[0,5,10,5,np.nan]})
print(data2)
print(data2.groupby('key').size())
print(data2.groupby('key').count())
  key  value
0   A    0.0
1   B    5.0
2   C   10.0
3   A    5.0
4   B    NaN
key
A    2
B    2
C    1
dtype: int64
     value
key       
A        2
B        1
C        1
#注意看结果中的细节

此外可以将group单独拿出来做一些操作。

#先分组,但是没有进行聚合操作,得到的是一个groupby对象
groups = data.groupby('key')
print(groups)
#可以仿照字典格式来遍历
for index,group in groups:
    print('---------------')
    print(index,'\n',group)
    print('-------------')
#当然也可以在遍历时加入一些特殊操作。
    print(group.mean())
---------------
A 
   key  value
0   A      0
3   A      5
6   A     10
-------------
value    5.0
dtype: float64
---------------
B 
   key  value
1   B      5
4   B     10
7   B     15
-------------
value    10.0
dtype: float64
---------------
C 
   key  value
2   C     10
5   C     15
8   C     20
-------------
value    15.0
dtype: float64

get_group()
使用这个方法可以选取想要的group,按键值索引

data3 = pd.DataFrame({'key':['A','B','A','B'],'value':[0,5,10,5]})
print(data3)
print(data3.groupby('key').get_group('A'))
print(data3.groupby('key').get_group('B'))
  key  value
0   A      0
1   B      5
2   A     10
3   B      5
  key  value
0   A      0
2   A     10
  key  value
1   B      5
3   B      5

多重索引
很多情况下我们都会遇到多重索引的问题,如何更好的去使用groupby呢,可以制定level参数

level = 0表示只使用第一重索引,level = 1表示只使用第二重索引,依次类推,也可以直接指定索引名称。

#这里先使用pandas的方法生成一个二级索引结构的series
arrays = [['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux'],
          ['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two']]
index = pd.MultiIndex.from_arrays(arrays,names = ['first','second'])
s = pd.Series(np.random.randn(8),index = index)
print(s)
print('------------------')
print(s.groupby(level =0).sum())
print('------------------')
print(s.groupby(level =1).sum())
print('------------------')
print(s.groupby(level ='first').sum())
first  second
bar    one      -2.102766
       two       0.967173
baz    one       0.177320
       two      -1.231985
foo    one      -0.192680
       two       0.793084
qux    one       1.140791
       two      -0.045113
dtype: float64
------------------
first
bar   -1.135594
baz   -1.054665
foo    0.600404
qux    1.095679
dtype: float64
------------------
second
one   -0.977335
two    0.483159
dtype: float64
------------------
first
bar   -1.135594
baz   -1.054665
foo    0.600404
qux    1.095679
dtype: float64

我这里只列举了很小的一部分操作,有时间要上官网仔细看看
groupby官网

自定义函数

pandas中可以使用apply,map,agg进行自定义函数操作,apply和agg操作的一行,map操作的是具体的一个值,但是apply()会将待处理的对象拆分成多个片段,然后对各片段调用传入的函数,最后尝试将各片段组合到一起,所以效率是最高的。注意groupby中最好就使用apply聚合,因为map不能,agg我测试时只用上了封装好的方法。

#自定义一个函数,作用是是将值加1
data3 = pd.DataFrame({'key':['A','B','A','B'],'value':[0,5,10,5]})
print(data3)
def add_1(value):
    return value + 1
print("value直接使用 add函数")
print(data3['value'].apply(add_1))
print(data3['value'].map(add_1))
print(data3['value'].agg(add_1))
print('groupby后使用 add函数聚合')
print(data3.groupby('key').apply(add_1))
print(data3.groupby('key').agg({'value':np.mean}))
  key  value
0   A      0
1   B      5
2   A     10
3   B      5
value直接使用 add函数
0     1
1     6
2    11
3     6
Name: value, dtype: int64
0     1
1     6
2    11
3     6
Name: value, dtype: int64
0     1
1     6
2    11
3     6
Name: value, dtype: int64
groupby后使用 add函数聚合
   value
0      1
1      6
2     11
3      6
     value
key       
A        5
B        5

熟练使用apply和map可以省去大量代码篇幅,同时效率也高,我列举一个我打天池O2O比赛的代码列子。
在这里我使用map方法和lambda函数处理就很方便的得到了结果

#将折扣卷转化为折扣率
off_train['discount_rate'] = off_train['Discount_rate'].map(lambda x : float(x) if ':' not in str(x) else
                                               (float(x.split(':')[0]) - float(x.split(':')[1])) / float(x.split(':')[0]))
off_train['discount_rate'].head()

apply官网

数据透视表

数据透视表(Pivot Table)是一种交互式的表,可以进行某些计算,如求和与计数等。所进行的计算与数据跟数据透视表中的排列有关。

之所以称为数据透视表,是因为可以动态地改变它们的版面布置,以便按照不同方式分析数据,也可以重新安排行号、列标和页字段。每一次改变版面布置时,数据透视表会立即按照新的布置重新计算数据。另外,如果原始数据发生更改,则可以更新数据透视表。
pandas使用pivot_table()方法生成数据透视表。

pivot_table(data, values=None, index=None, columns=None, aggfunc='mean', fill_value=None, margins=False, dropna=True, margins_name='All', observed=False)

data即为要操作的dataframe,values为需要进行操作的列,index为行索引,columns为列索引,aggfunc为进行聚合的操作,默认为均值,fill_value默认不填充缺失值,可设置填充,margins设为true的话会添加所有的行和列,这里的dropna是删除全为nan的列。

一个简单的求和例子:

#生成一个dataframe
df = pd.DataFrame({"A": ["foo", "foo", "foo", "foo", "foo",
                         "bar", "bar", "bar", "bar"],
                   "B": ["one", "one", "one", "two", "two",
                         "one", "one", "two", "two"],
                   "C": ["small", "large", "large", "small",
                         "small", "large", "small", "small",
                          "large"],
                   "D": [1, 2, 2, 3, 3, 4, 5, 6, 7],
                   "E": [2, 4, 5, 5, 6, 6, 8, 9, 9]})
print(df)
print('--------------------------')
#将A,B作为行索引,C作为列索引,对D进行求和操作
print(pd.pivot_table(df, values='D', index=['A', 'B'],
                  columns=['C'], aggfunc=np.sum))
     A    B      C  D  E
0  foo  one  small  1  2
1  foo  one  large  2  4
2  foo  one  large  2  5
3  foo  two  small  3  5
4  foo  two  small  3  6
5  bar  one  large  4  6
6  bar  one  small  5  8
7  bar  two  small  6  9
8  bar  two  large  7  9
--------------------------
C        large  small
A   B                
bar one    4.0    5.0
    two    7.0    6.0
foo one    4.0    1.0
    two    NaN    6.0

填充缺失值为0:

print(pd.pivot_table(df, values='D', index=['A', 'B'],
                     columns=['C'], aggfunc=np.sum, fill_value=0))
C        large  small
A   B                
bar one      4      5
    two      7      6
foo one      4      1
    two      0      6

在多个列中取多种聚合情况:

print(pd.pivot_table(df, values=['D', 'E'], index=['A', 'C'],
                   aggfunc={'D': np.mean,
                            'E': [min, max, np.mean]}))
                  D   E              
               mean max      mean min
A   C                                
bar large  5.500000   9  7.500000   6
    small  5.500000   9  8.500000   8
foo large  2.000000   5  4.500000   4
    small  2.333333   6  4.333333   2

更多方法详见pivot_table官网

时间序列操作

时间戳

#生成时间戳
import pandas as pd
ts = pd.Timestamp('2019-8-19')
print(ts)
2019-08-19 00:00:00

时间序列

pandas.date_range(start=None, end=None, periods=None, freq=None, tz=None, normalize=False, name=None, closed=None, **kwargs)[source]

#生成时间序列
#生成时间序列,periods表示往后10次,freq默认为一天,设置为12H表示没12H一次
print(pd.Series(pd.date_range(start='2019-8-19',periods = 10,freq = '12H')))
0   2019-08-09 00:00:00
1   2019-08-09 12:00:00
2   2019-08-10 00:00:00
3   2019-08-10 12:00:00
4   2019-08-11 00:00:00
5   2019-08-11 12:00:00
6   2019-08-12 00:00:00
7   2019-08-12 12:00:00
8   2019-08-13 00:00:00
9   2019-08-13 12:00:00
dtype: datetime64[ns]

转换为时间格式

pandas.to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False, utc=None, box=True, format=None, exact=True, unit=None, infer_datetime_format=False, origin='unix', cache=True)
这里对参数不做解释,有兴趣去官网查看to_datetime官网

#转换为时间格式
s = pd.Series(['2017-11-24 00:00:00','2017-11-25 00:00:00','2017-11-26 00:00:00'])
print(s)
ts = pd.to_datetime(s)
print(ts)
0    2017-11-24 00:00:00
1    2017-11-25 00:00:00
2    2017-11-26 00:00:00
dtype: object
0   2017-11-24
1   2017-11-25
2   2017-11-26
dtype: datetime64[ns]

下面我加入数据集讲时间序列的操作
数据集:
链接: https://pan.baidu.com/s/1X9jvXoxlaLDNqww4sa8P1A 密码: e20i

#读入数据集
data = pd.read_csv('./data/flowdata.csv')
print(data.head())
                  Time   L06_347  LS06_347  LS06_348
0  2009-01-01 00:00:00  0.137417  0.097500  0.016833
1  2009-01-01 03:00:00  0.131250  0.088833  0.016417
2  2009-01-01 06:00:00  0.113500  0.091250  0.016750
3  2009-01-01 09:00:00  0.135750  0.091500  0.016250
4  2009-01-01 12:00:00  0.140917  0.096167  0.017000

可以看到这个数据的第一列是时间
我们将其转换为时间格式并将它当成索引

#将Time列转换为时间格式
data['Time'] = pd.to_datetime(data['Time'])
#设置Time列为索引
data = data.set_index('Time')
print(data.head())
                      L06_347  LS06_347  LS06_348
Time                                             
2009-01-01 00:00:00  0.137417  0.097500  0.016833
2009-01-01 03:00:00  0.131250  0.088833  0.016417
2009-01-01 06:00:00  0.113500  0.091250  0.016750
2009-01-01 09:00:00  0.135750  0.091500  0.016250
2009-01-01 12:00:00  0.140917  0.096167  0.017000

上述方法太麻烦了,我们可以在读入数据时就对其进行操作,使用index_col和parse_dates参数

data = pd.read_csv('./data/flowdata.csv',index_col = 'Time',parse_dates = True)
print(data.head())
                      L06_347  LS06_347  LS06_348
Time                                             
2009-01-01 00:00:00  0.137417  0.097500  0.016833
2009-01-01 03:00:00  0.131250  0.088833  0.016417
2009-01-01 06:00:00  0.113500  0.091250  0.016750
2009-01-01 09:00:00  0.135750  0.091500  0.016250
2009-01-01 12:00:00  0.140917  0.096167  0.017000

使用时间索引

#时间切片
print(data[('2012-01-01 09:00'):('2012-01-01 19:00')])
#直接索引年份
print(data['2013'])
#索引2012年1到2月的数据
print(data['2012-01':'2012-02'].head(10))
                      L06_347  LS06_347  LS06_348
Time                                             
2012-01-01 09:00:00  0.330750  0.293583  0.029750
2012-01-01 12:00:00  0.295000  0.285167  0.031750
2012-01-01 15:00:00  0.301417  0.287750  0.031417
2012-01-01 18:00:00  0.322083  0.304167  0.038083

                      L06_347  LS06_347  LS06_348
Time                                             
2013-01-01 00:00:00  1.688333  1.688333  0.207333
2013-01-01 03:00:00  2.693333  2.693333  0.201500
2013-01-01 06:00:00  2.220833  2.220833  0.166917
2013-01-01 09:00:00  2.055000  2.055000  0.175667
2013-01-01 12:00:00  1.710000  1.710000  0.129583
2013-01-01 15:00:00  1.420000  1.420000  0.096333
2013-01-01 18:00:00  1.178583  1.178583  0.083083
2013-01-01 21:00:00  0.898250  0.898250  0.077167
2013-01-02 00:00:00  0.860000  0.860000  0.075000

                      L06_347  LS06_347  LS06_348
Time                                             
2012-01-01 00:00:00  0.307167  0.273917  0.028000
2012-01-01 03:00:00  0.302917  0.270833  0.030583
2012-01-01 06:00:00  0.331500  0.284750  0.030917
2012-01-01 09:00:00  0.330750  0.293583  0.029750
2012-01-01 12:00:00  0.295000  0.285167  0.031750
2012-01-01 15:00:00  0.301417  0.287750  0.031417
2012-01-01 18:00:00  0.322083  0.304167  0.038083
2012-01-01 21:00:00  0.355417  0.346500  0.080917
2012-01-02 00:00:00  1.069333  0.970000  0.071917
2012-01-02 03:00:00  0.886667  0.817417  0.070833


此外还可以使用bool类型索引

#索引所有1月的数据
print(data[data.index.month == 1].head())
#索引8点到12点的数据
print(data[(data.index.hour > 8) & (data.index.hour <12)].head())
#上一个用法也可以直接使用between_time方法
print(data.between_time('08:00','12:00').head())
                      L06_347  LS06_347  LS06_348
Time                                             
2009-01-01 00:00:00  0.137417  0.097500  0.016833
2009-01-01 03:00:00  0.131250  0.088833  0.016417
2009-01-01 06:00:00  0.113500  0.091250  0.016750
2009-01-01 09:00:00  0.135750  0.091500  0.016250
2009-01-01 12:00:00  0.140917  0.096167  0.017000

                      L06_347  LS06_347  LS06_348
Time                                             
2009-01-01 09:00:00  0.135750  0.091500  0.016250
2009-01-02 09:00:00  0.141917  0.097083  0.016417
2009-01-03 09:00:00  0.124583  0.084417  0.015833
2009-01-04 09:00:00  0.109000  0.105167  0.018000
2009-01-05 09:00:00  0.161500  0.114583  0.021583

                      L06_347  LS06_347  LS06_348
Time                                             
2009-01-01 09:00:00  0.135750  0.091500  0.016250
2009-01-01 12:00:00  0.140917  0.096167  0.017000
2009-01-02 09:00:00  0.141917  0.097083  0.016417
2009-01-02 12:00:00  0.147833  0.101917  0.016417
2009-01-03 09:00:00  0.124583  0.084417  0.015833

时间序列重采样
resample方法类似于groupby,只是这个方法是针对时间进行聚合运算

DataFrame.resample(self, rule, how=None, axis=0, fill_method=None, closed=None, label=None, convention='start', kind=None, loffset=None, limit=None, base=0, on=None, level=None)
官网

#按天求均值
print(data.resample('D').mean().head())
#按年求和
print(data.resample('Y').sum().head())
             L06_347  LS06_347  LS06_348
Time                                    
2009-01-01  0.125010  0.092281  0.016635
2009-01-02  0.124146  0.095781  0.016406
2009-01-03  0.113562  0.085542  0.016094
2009-01-04  0.140198  0.102708  0.017323
2009-01-05  0.128812  0.104490  0.018167

               L06_347     LS06_347   LS06_348
Time                                          
2009-12-31  640.507333   639.476750  71.634750
2010-12-31  994.468583  1029.896542  75.620100
2011-12-31  704.901583   692.756488  49.946350
2012-12-31  669.086250   659.646667  64.412572
2013-12-31   14.724333    14.724333   1.212583

字符串操作

pandas.Series.str中封装了许多处理字符串类型列的方法,合理运用的话能达到很好的效果。

one hot 独热编码,get_dummies
series=data['列名'].str.get_dummies(sep=',')

s = pd.Series(['a','a|b','a|c'])
print(s.str.get_dummies(sep = '|'))
   a  b  c
0  1  0  0
1  1  1  0
2  1  0  1

切分字符串,split()
series=data['列名'].str.split(',')
把DataFrame列中字符串以','分隔开,每个元素分开后存入一个列表里
series=data['列名'].str.split(',',expand=True)
参数expand,这个参数取True时,会把切割出来的内容当做一列,产生多列。

s = pd.Series(['a_b_C','c_d_e','f_g_h'])
print(s.str.split('_'))
print(s.str.split('_',expand = True))
0    [a, b, C]
1    [c, d, e]
2    [f, g, h]
dtype: object
   0  1  2
0  a  b  C
1  c  d  e
2  f  g  h

3.替换,replace()
series=data['列名'].str.replace(',','-')

#替换列名
df = pd.DataFrame(np.random.randn(3,2),columns = ['A a','B b'],index = range(3))
df.columns = df.columns.str.replace(' ','_')
print(df)
        A_a       B_b
0 -0.634455 -0.255455
1 -1.189211  0.659666
2 -0.310230  0.200333

4.是否包含表达式,contains()
series=data['列名'].str.contains('we')
返回的是布尔值series

s = pd.Series(['A','Aas','Afgew','Ager','Agre','Ager'])
print(s.str.contains('Ag'))
0    False
1    False
2    False
3     True
4     True
5     True
dtype: bool

5.查找所有符合正则表达式的字符findall()
series=data['列名'].str.findall("[a-z]")
以数组的形式返回

s = pd.Series(['A','Aas','Afgew','Ager','Agre','Ager'])
print(s.str.findall('Ag'))
0      []
1      []
2      []
3    [Ag]
4    [Ag]
5    [Ag]
dtype: object

此外还有以下方法等,就不一样列举了。

len(),计算字符串的长度
series=data['列名'].str.len()
strip(),去除前后的空白字符
series=data['列名'].str.strip()
rstrip() 去除后面的空白字符
lstrip() 去除前面的空白字符

isalnum() 是否全部是数字和字母组成
isalpha() 是否全部是字母
isdigit() 是否全部都是数字
isspace() 是否空格
islower() 是否全部小写
isupper() 是否全部大写
istitle() 是否只有首字母为大写,其他字母为小写

这一篇介绍了一些pandas的高级用法,下一篇讲讲运用pandas内置函数画图的实例。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容