• 学机器学习,不会数据处理怎么行?—— 二、Pandas详解


    在上篇文章学机器学习,不会数据处理怎么行?—— 一、NumPy详解中,介绍了NumPy的一些基本内容,以及使用方法,在这篇文章中,将接着介绍另一模块——Pandas。(本文所用代码在这里

    Pandas数据结构介绍

    大家应该都听过表结构,但是,如果让你自己来实现这么一个结构,并且能对其进行数据处理,能实现吗?我相信,大部分人都能做出来,但是不一定能做的很好。而Python中的一个模块pandas给我们提供了一个很好的数据结构,它包括了序列Series和数据框DataFrame。pandas是基于NumPy数组构建的,特别是基于数组的函数和不使用for循环的数据处理,让以Numpy为中心的应用变得更加简单。

    Series

    创建方式

    Series是一种类似于一维数组的对象,它由一组数据(各种NumPy数据类型)以及一组与之相关的数据标签(即索引)组成,其创建主要有三种方式

    1)通过一维数组创建序列

    import numpy as np
    import pandas as pd
    arr1 = np.arange(10)
    s1 = pd.Series(arr1)

    通过type函数可查看arr1与s1的类型分别为,numpy.ndarray 和 pandas.core.series.Series。

    3)通过字典的方式创建

    dic1 = {'a': 1,'b': 2,'c': 3,'d': 4,'e': 50}
    s2 = pd.Series(dic1)

    通过type函数可查看dic1与s2的类型分别为,dict 和 pandas.core.series.Series。

    3)通过DataFrame中的某一行或者某一列创建序列

    这一步将在下面讲DataFrame的时候再补充

    Series索引

    我们上面在创建Series序列时,可以输出查看我们创建的Series长什么样,发现有两列,第二列的数据跟我们输入的是一样的,那第一列是什么呢?那就是索引。Series的字符串表现形式为:索引在左边,值在右边。如果我们没有为数据指定索引,就会自动创建一个0到N-1(N为数据的长度)的整数型索引。

     我们可以通过 Series 的 values 和 index 属性获取其数组表示形式和索引对象。

    obj = pd.Series([6, 9, -8, 1])
    obj.values
    obj.index

    我们也能修改其索引

    obj.index = ['a','b','c','d']

    讲了那么多,知道这是索引了,但它有什么用呢? 

    1.我们可以通过索引值或索引标签进行数据的获取

    obj['a']
    obj[3]
    obj[[0,2,3]]
    obj[0:2]
    obj['a':'c']

    注:若通过索引标签获取数据的话,末端标签对应的值也将返回!!!

    2.自动化对齐

    如果有两个序列,需要对这两个序列进行算术运算,这时索引的存在就体现的它的价值了—自动化对齐.

    obj1 = pd.Series(np.array([10,15,20,30,55,80]),index = ['a','b','c','d','e','f'])
    obj2 = pd.Series(np.array([12,11,13,15,14,16]),index = ['a','c','g','b','d','f'])
    obj1+obj2
    obj1*obj2

    我们会发现,计算后的序列号中,g和e对应的值为NaN,这是因为obj1中没有g索引,obj2中没有e索引,所以数据的运算会产生两个缺失值NaN。

    DataFrame

    DataFrame是一个表格型的数据结构,它含有一组有序的列,每列可以是不同的值类型(数值、字符串、布尔值等)。DataFrame既有行索引也有列索引,它可以被看做由Series组成的字典。

    创建方式

    1)通过二维数组创建

    arr2 = np.array(np.arange(12).reshape(4,3))
    df1 = pd.DataFrame(arr2)

    2)通过字典的方式创建

    该方式可通过字典列表和嵌套字典实现,如果将嵌套字典传给DataFrame,pandas就会被解释为:外层字典的键作为列,内层键则作为行索引

    #字典列表
    dic2 = {'a': [1, 2, 3, 4],'b': [5, 6, 7, 8],'c': [9, 10, 11, 12],'d': [13, 14, 15, 16]}
    df2 = pd.DataFrame(dic2)
    
    #嵌套字典
    dic3 = {'one': {'a': 1,'b': 2,'c': 3,'d':4},
            'two': {'a': 5,'b': 6,'c': 7,'d': 8},
            'three':{'a': 9,'b': 10,'c': 11,'d':12}}
    df3 = pd.DataFrame(dic3)

    3)通过数据框的方式创建

    df4 = df3[['one','three']]
    
    s3 = df4['one']

    DataFrame索引

    DataFrame的索引与Series的索引大体上是一样的,不过DataFrame有两个索引,分别是列索引和行索引,感觉看起来就跟excel差不多了。具体的实现方式可通过下面的部分来了解。

    利用pandas进行数据处理

    pandas可以通过布尔索引有针对的选取原数据的子集、指定行、指定列等,同时我们可以通过 pd.read_csv()来导入数据集,因为暂时找不到数据集,就从别人的代码里复制一些过来了

    data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada', 'Nevada'],
            'year': [2000, 2001, 2002, 2001, 2002, 2003],
            'pop': [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]}
    frame = pd.DataFrame(data)

    选择数据

    简单筛选

    我们可以使用以下函数来查询数据的前几行或后几行(默认5行)

    frame.head()
    frame.tail()

    也可以使用行列索引来简单地查询指定的行和列

    frame['state']
    frame[0:3]

    loc

    当然,上面的方式只是比较简单的操作,复杂些的,我们可以通过 loc 来选取数据(: 表示所有行)

    frame.loc[:,['state','year']]

    iloc

    另外,我们也可以使用iloc,通过位置选择在不同情况下所需要的数据,可进行选取某个数据、连续或者跨行选择等操作

    frame.iloc[0:2,0:2]

    ix

    除此之外还有一种操作方式,使用 混合选择 ix ,我这里的表可能无法很好的体现,如果将行索引改为字符就可以看出来了

    frame.ix[3:5,['year']]

    发现输入这行代码运行之后发出一条警告

    DeprecationWarning: 
    .ix is deprecated. Please use
    .loc for label based indexing or
    .iloc for positional indexing

    emmmmm。。想想以后主要还是用 loc 和 iloc 好了。

    通过判断的筛选

    除了上面讲的之外,我们还可以通过判断指令进行选择,即通过约束某项条件然后选择出当前所有数据。

    frame[frame['year']>2001]

    数据修改

    讲完了数据选择,既然选择出了数据,我们可能会想去修改数据,那么,怎么修改呢?

    根据位置

    我们可以利用索引或者标签确定需要修改的位置

    frame.iloc[0,2]=1.6
    frame.loc[1,'pop']=2

    根据条件

    如果我们想将数据中'pop'栏小于1.7的'year'改为1999,那么我们可以进行如下操作

    frame.year[frame['pop']<1.7]=1999

    按行或按列设置

    我们还可以对行列进行批处理

    frame['pop']=2.6

    添加和删除数据

    上面讲的都是数据的修改,再来讲讲数据的添加和删除

    #数据添加
    #方法一
    frame['A'] = np.nan
    #方法二
    frame['B'] = pd.Series([1, 2, 3, 4, 5, 6])
    
    #数据删除 
    frame.drop('B',axis = 1)

    注:

    • 对于方法二,长度必须对齐,如果行索不为默认则需指定索引
    dates = pd.date_range('20130101', periods=6)
    df = pd.DataFrame(np.arange(24).reshape((6,4)),index=dates, columns=['A','B','C','D'])
    df['E'] = pd.Series([1, 2, 3, 4, 5, 6],index=pd.date_range('20130101',periods=6))
    • 对于数据删除,axis取值 0表示删除行索引 1表示删除列索引 默认为0

    统计分析

    pandas模块为我们提供了非常多的描述性统计分析的指标函数,如总和、均值、最小值、最大值等,跟对应的NumPy数组方法相比,它们都是基于没有缺失数据的假设而构建的,下图中列举了大部分函数

    这里有几点需要注意

    • descirbe函数只能针对序列或者数据框,一维数组是没有这个方法的。
    • 使用sum时,NaN值会自动被排除,除非整个切片都是NaN,通过skipna选项可以禁用该功能。
    • 调用sum时,返回的时含有列的和的Series,需要传入axis的值才会进行行运算

    当我们要对一列数据进行分析时,我们可以将要分析的内容封装成一个函数,方便调用

    def stats(x):
        return pd.Series([x.count(),x.min(),x.idxmin(),
        x.quantile(.25),x.median(),
        x.quantile(.75),x.mean(),
        x.max(),x.idxmax(),
        x.mad(),x.var(),
        x.std(),x.skew(),x.kurt()],
        index = ['Count','Min','Whicn_Min',
        'Q1','Median','Q3','Mean',
        'Max','Which_Max','Mad',
        'Var','Std','Skew','Kurt'])

    有时或许会需要对一个数据框进行分析,那该如何操作?是一列一列不断地取出来然后调用函数吗?这个方法也不是不可行,但面对众多数据时该怎么办?一个一个调用怕不是要弄到明年,在这里我们可以使用apply函数

    d1 = pd.Series(2*np.random.normal(size = 100)+3)
    d2 = np.random.f(2,4,size = 100)
    d3 = np.random.randint(1,100,size = 100)
    df = pd.DataFrame(np.array([d1,d2,d3]).T,columns=['x1','x2','x3'])
    df.head()
    df.apply(stats)

    就这样很简单的创建了数值型数据的统计性描述。但如果是离散型数据呢?就不能用这个统计口径了,我们需要统计离散变量的观测数、唯一值个数、众数水平及个数。你只需要使用describe方法就可以实现这样的统计了。

    缺失值处理

    现实生活中的数据是非常杂乱的,其中缺失值也是非常常见的,对于缺失值的存在可能会影响到后期的数据分析或挖掘工作,那么我们该如何处理这些缺失值呢?常用的有三大类方法,即删除法、填补法和插值法。

    删除法:当数据中的某个变量大部分值都是缺失值,可以考虑删除改变量;当缺失值是随机分布的,且缺失的数量并不是很多是,也可以删除这些缺失的观测。
    替补法:对于连续型变量,如果变量的分布近似或就是正态分布的话,可以用均值替代那些缺失值;如果变量是有偏的,可以使用中位数来代替那些缺失值;对于离散型变量,我们一般用众数去替换那些存在缺失的观测。
    插补法:插补法是基于蒙特卡洛模拟法,结合线性模型、广义线性模型、决策树等方法计算出来的预测值替换缺失值。

    删除法

    这里仅讲述删除法和替补法,先来看删除法,先建立一个6X4的矩阵数据并且把两个位置置为空

    dates = pd.date_range('20130101', periods=6)
    df = pd.DataFrame(np.arange(24).reshape((6,4)),index=dates, columns=['A','B','C','D'])
    df.iloc[0,1] = np.nan
    df.iloc[1,2] = np.nan

    我们可以结合sum函数和isnull函数来检测数据中含有多少缺失值

    for i in df.columns:
        print(sum(pd.isnull(df[i])))

    也可以通过any来判断是否存在缺失值

    np.any(df.isnull()) == True  

    删除直接dropna就行了

    df.dropna(
        axis=0,     # 0: 对行进行操作; 1: 对列进行操作 默认为0
        how='any'   # 'any': 只要存在 NaN 就 drop 掉; 'all': 必须全部是 NaN 才 drop 默认为'any'
        ) 

    填补法

    简单粗暴的办法就是把所有的NaN用0代替

    df.fillna(value=0)

    稍微好一点的方法,使用常量填充不同的列

    df.fillna({'B': 3,'C': 4})

    还算好的方法,采用前项填充或后向填充

    df.fillna(method = 'ffill') #用前一个观测值填充
    df.fillna(method = 'bfill') #用后一个观测值填充

    较好的方法,用均值或中位数填充各自的列

    df.fillna(df.median())
    df.fillna(df.mean())

    很显然,在使用填充法时,相对于常数填充或前项、后项填充,使用各列的众数、均值或中位数填充要更加合理一点,这也是工作中常用的一个快捷手段。

    注:使用fillna,dropna时,需要添加参数 inplace = True,如df.fillna(df.median(),inplace = True),以确认修改,否则实际的数据并不会有改动。

    数据合并

    concat

    pandas 处理多组数据的时候往往会要用到数据的合并处理,使用 concat 是一种基本的合并方式.而且concat中有很多参数可以调整,合并成你想要的数据形式.

    先创建数据集

    df1 = pd.DataFrame(np.ones((3,4))*0, columns=['a','b','c','d'])
    df2 = pd.DataFrame(np.ones((3,4))*1, columns=['a','b','c','d'])
    df3 = pd.DataFrame(np.ones((3,4))*2, columns=['a','b','c','d'])

    concat合并

    pd.concat([df1, df2, df3], axis=0) #纵向合并
    pd.concat([df1, df2, df3], axis=1) #横向合并

    这里观察下纵向合并的输出,会发现index的值为0,1,2,0,1,2,0,1,2,大部分情况下,这肯定不是我们想要的,我们可以通过一下方法来重置index

    pd.concat([df1, df2, df3], axis=0, ignore_index=True)

    pandas中的数据结构是支持类似SQL的部分操作方式的,如添加新行新列、多表连接、聚合什么的。pandas有个join参数,用来定义连接方式的,默认为outer,此方式是依照column来做纵向合并有相同的column上下合并在一起,其他的独自成列,没有的值用NaN填充

    df1 = pd.DataFrame(np.ones((3,4))*0, columns=['a','b','c','d'], index=[1,2,3])
    df2 = pd.DataFrame(np.ones((3,4))*1, columns=['b','c','d','e'], index=[2,3,4])
    res = pd.concat([df1, df2], axis=0, join='outer')#纵向"外"合并df1与df2

    参数:

    • axis 表示行或列,0为行,1为列
    • join 'inner':内连接,会产生NaN的列丢弃 ‘outer’:外连接,上面介绍过了

    另外,还有join_axes也可以决定连接方式

    pd.concat([df1, df2], axis=1, join_axes=[df1.index])
    pd.concat([df1, df2], axis=1, join_axes=[df2.index])

    运行上面两行代码,就会发现有所不同,第一行是以df1的index为依据,将df2中具有相同索引的行对应拼接上去,第二行同样的道理。

    此外,还有append操作,该操作类似与axis=0,join='outer'时的操作,不过要注意的是append只有纵向合并。

    merge

    pandas中的合并操作除了concat之外,还有merge操作,主要用于两组有key column的数据,统一索引的数据. 通常也被用在Database的处理当中.

    看个简单的,依据一组Key合并

    left = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3'],
                                 'A': ['A0', 'A1', 'A2', 'A3'],
                                 'B': ['B0', 'B1', 'B2', 'B3']})
    right = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3'],
                                  'C': ['C0', 'C1', 'C2', 'C3'],
                                  'D': ['D0', 'D1', 'D2', 'D3']})
    pd.merge(left, right, on='key')

    稍微难一点就是依据两组Key合并,合并时有四种方法 how=['left','right','outer','inner'],默认为how='inner'

    left = pd.DataFrame({'key1': ['K0', 'K0', 'K1', 'K2'],
                          'key2': ['K0', 'K1', 'K0', 'K1'],
                          'A': ['A0', 'A1', 'A2', 'A3'],
                          'B': ['B0', 'B1', 'B2', 'B3']})
    right = pd.DataFrame({'key1': ['K0', 'K1', 'K1', 'K2'],
                           'key2': ['K0', 'K0', 'K0', 'K0'],
                           'C': ['C0', 'C1', 'C2', 'C3'],
                           'D': ['D0', 'D1', 'D2', 'D3']})
    
    res = pd.merge(left, right, on=['key1', 'key2'], how='inner')
    print(res)
    
    res = pd.merge(left, right, on=['key1', 'key2'], how='outer')
    print(res)
    
    res = pd.merge(left, right, on=['key1', 'key2'], how='left')
    print(res)
    
    res = pd.merge(left, right, on=['key1', 'key2'], how='right')
    print(res)

    都试一试然后输出就知道是如何操作的了。

     上面讲的是以列索引为标准进行合并,当然,我们还可以以index为标准进行合并

    left = pd.DataFrame({'A': ['A0', 'A1', 'A2'],
                         'B': ['B0', 'B1', 'B2']},
                         index=['K0', 'K1', 'K2'])
    right = pd.DataFrame({'C': ['C0', 'C2', 'C3'],
                          'D': ['D0', 'D2', 'D3']},
                         index=['K0', 'K2', 'K3'])
    
    res = pd.merge(left, right, left_index=True, right_index=True, how='outer')
    print(res)
    
    res = pd.merge(left, right, left_index=True, right_index=True, how='inner')
    print(res)

     聚合和分组

    pandas提供了一个灵活高效的groupby功能,它使你能以一种自然的方式对数据集进行切片、切块、摘要等操作。根据一个或多个键(可以是函数、数组或DataFrame列名)拆分pandas对象。计算分组摘要统计,如计数、平均值、标准差,或用户自定义函数。

    根据列索引分组

    df = pd.DataFrame({'key1':['a', 'a', 'b', 'b', 'a'],
            'key2':['one', 'two', 'one', 'two', 'one'],
            'data1':np.random.randn(5),
            'data2':np.random.randn(5)})
    df['data1'].groupby(df['key1']).mean()

    这里我们按照key1将数据分组,然后计算分组后的data1的平均值。

    我们也可以一次传入多个数组

    df['data1'].groupby([df['key1'], df['key2']]).mean()

    此外,还可以将列名用作分组

    df.groupby('key1').mean()
    df.groupby(['key1', 'key2']).mean()

    注:这里在执行df.groupby('key1').mean()时,结果中没有key2列。这是因为df['key2']不是数值数据,所以被从结果中排除了。默认情况下,所有数值列都会被聚合,虽然有时可能会被过滤为一个子集。

    分组后可进行迭代

    for name, group in df.groupby('key1'):
        print(name)
        print(group)

    对于多重键的情况

    for (k1, k2), group in df.groupby(['key1', 'key2']):
         print (k1, k2)
         print (group)

     上面进行的group操作返回的类型都是Series,如果我们想返回DataFrame类型的结果,那么我们可以进行如下操作

    df[['data2']].groupby([df['key1']])

    根据行类型进行分组

     groupby默认是在axis=0上进行分组的,通过设置也可以在其他任何轴上进行分组。拿上面例子中的df来说,我们可以根据行类型(dtype)对列进行分组

    dict(list(df.groupby(df.dtypes,axis = 1)))

    这方面还有蛮多内容,不过都还没接触过,只能暂时讲到这里了,如果要了解更多,可以访问这篇博客 ,我在后面也会逐渐完善这些方面的内容。

    结尾

    讲了这么多,相信你对pandas肯定有了一定的了解了,但由于个人接触不多,就暂时介绍到这里,pandas还有很多内容如数据透视表、多层索引的使用、数据清洗等,将在后面学到时再做补充

  • 相关阅读:
    2.Android之按钮Button和编辑框EditText学习
    《DSP using MATLAB》Problem 3.8
    《DSP using MATLAB》Problem 3.7
    《DSP using MATLAB》Problem 3.6
    《DSP using MATLAB》Problem 3.5
    《DSP using MATLAB》Problem 3.4
    《DSP using MATLAB》Problem 3.3
    《DSP using MATLAB》Problem 3.2
    《DSP using MATLAB》Problem 3.1
    《DSP using MATLAB》Problem 2.20
  • 原文地址:https://www.cnblogs.com/csu-lmw/p/9913546.html
Copyright © 2020-2023  润新知