美文网首页
机器学习入门笔记二 pandas高级操作

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

作者: 一只当归 | 来源:发表于2019-08-19 17:34 被阅读0次

    这篇主要介绍一些我觉得常用的一些高级用法,主要包括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内置函数画图的实例。

    相关文章

      网友评论

          本文标题:机器学习入门笔记二 pandas高级操作

          本文链接:https://www.haomeiwen.com/subject/qfzjsctx.html