• 数据分析三剑客之Pandas


    1.引入

    前面一篇文章我们介绍了numpy,但numpy的特长并不是在于数据处理,而是在它能非常方便地实现科学计算,所以我们日常对数据进行处理时用的numpy情况并不是很多,我们需要处理的数据一般都是带有列标签和index索引的,而numpy并不支持这些,这时我们就需要pandas上场啦! 

    2.WHAT?

    Pandas是基于Numpy构建的库,在数据处理方面可以把它理解为numpy加强版,同时Pandas也是一项开源项目 。不同于numpy的是,pandas拥有种数据结构:SeriesDataFrame: 

    下面我们就来生成一个简单的series对象来方便理解:

    1. In [1]: from pandas import Series,DataFrame
    2. In [2]: import pandas as pd
    3. In [3]: data = Series([1,2,3,4],index = ['a','b','c','d'])
    4. In [4]: data
    5. Out[4]:
    6. a 1
    7. b 2
    8. c 3
    9. d 4
    10. dtype: int64

    Series是一种类似一维数组的数据结构,由一组数据和与之相关的index组成,这个结构一看似乎与dict字典差不多,我们知道字典是一种无序的数据结构,而pandas中的Series的数据结构不一样,它相当于定长有序的字典,并且它的index和value之间是独立的,两者的索引还是有区别的,Series的index变的,而dict字典的key值是不可变的。

    下面照例生成一个简单的DataFrame对象:

    1. In [8]: data = {'a':[1,2,3],'b':['we','you','they'],'c':['btc','eos','ae']}
    2. In [9]: df = DataFrame(data)
    3. In [10]: df
    4. Out[10]:
    5. a b c
    6. 0 1 we btc
    7. 1 2 you eos
    8. 2 3 they ae

    DataFrame这种数据结构我们可以把它看作是一张二维表,DataFrame长得跟我们平时使用的Excel表格差不多,DataFrame的横行称为columns,竖列和Series一样称为index,DataFrame每一列可以是不同类型的值集合,所以DataFrame你也可以把它视为不同数据类型同一index的Series集合。

    3.WHY?

    科学计算方面numpy是优势,但在数据处理方面DataFrame就更胜一筹了,事实上DataFrame已经覆盖了一部分的数据操作了,对于数据挖掘来说,工作可大概分为读取数据-数据清洗-分析建模-结果展示:

    先说说读取数据,Pandas提供强大的IO读取工具,csv格式、Excel文件、数据库等都可以非常简便地读取,对于大数据,pandas也支持大文件的分块读取;

    接下来就是数据清洗,面对数据集,我们遇到最多的情况就是存在缺失值,Pandas把各种类型数据类型的缺失值统一称为NaN(这里要多说几句,None==None这个结果是true,但np.nan==np.nan这个结果是false,NaN在官方文档中定义的是float类型,有关于NaN和None的区别以及使用,有位博主已经做好整理:None vs NaN),Pandas提供许多方便快捷的方法来处理这些缺失值NaN。

    最重要的分析建模阶段,Pandas自动且明确的数据对齐特性,非常方便地使新的对象可以正确地与一组标签对齐,有了这个特性,Pandas就可以非常方便地将数据集进行拆分-重组操作。

    最后就是结果展示阶段了,我们都知道Matplotlib是个数据视图化的好工具,Pandas与Matplotlib搭配,不用复杂的代码,就可以生成多种多样的数据视图。

    4.HOW?

    Series

    Series的两种生成方式:

    1. In [19]: data = Series([222,'btc',234,'eos'])
    2. In [20]: data
    3. Out[20]:
    4. 0 222
    5. 1 btc
    6. 2 234
    7. 3 eos
    8. dtype: object

    虽然我们在生成的时候没有设置index值,但Series还是会自动帮我们生成index,这种方式生成的Series结构跟list列表差不多,可以把这种形式的Series理解为竖起来的list列表。

    1. In [21]: data = Series([1,2,3,4],index = ['a','b','c','d'])
    2. In [22]: data
    3. Out[22]:
    4. a 1
    5. b 2
    6. c 3
    7. d 4
    8. dtype: int64

    这种形式的Series可以理解为numpy的array外面披了一件index的马甲,所以array的相关操作,Series同样也是支持的。结构非常相似的dict字典同样也是可以转化为Series格式的:

    1. In [29]: dic = {'a':1,'b':2,'c':'as'}
    2. In [30]: dicSeries = Series(dic)

    查看Series的相关信息:

    1. In [32]: data.index
    2. Out[32]: Index(['a', 'b', 'c', 'd'], dtype='object')
    3. In [33]: data.values
    4. Out[33]: array([1, 2, 3, 4], dtype=int64)
    5. In [35]: 'a' in data #in方法默认判断的是index值
    6. Out[35]: True

    Series的NaN生成:

    1. In [46]: index1 = [ 'a','b','c','d']
    2. In [47]: dic = {'b':1,'c':1,'d':1}
    3. In [48]: data2 = Series(dic,index=index1)
    4. In [49]: data2
    5. Out[49]:
    6. a NaN
    7. b 1.0
    8. c 1.0
    9. d 1.0
    10. dtype: float64

    从这里我们可以看出Series的生成依据的是index值,index‘a’在字典dic的key中并不存在,Series自然也找不到’a’的对应value值,这种情况下Pandas就会自动生成NaN(not a number)来填补缺失值,这里还有个有趣的现象,原本dtype是int类型,生成NaN后就变成了float类型了,因为NaN的官方定义就是float类型

    NaN的相关查询:

    1. In [58]: data2.isnull()
    2. Out[58]:
    3. a True
    4. b False
    5. c False
    6. d False
    7. dtype: bool
    8. In [59]: data2.notnull()
    9. Out[59]:
    10. a False
    11. b True
    12. c True
    13. d True
    14. dtype: bool
    15. In [60]: data2[data2.isnull()==True] #嵌套查询NaN
    16. Out[60]:
    17. a NaN
    18. dtype: float64
    19. In [64]: data2.count() #统计非NaN个数
    20. Out[64]: 3

    切记切记,查询NaN值切记不要使用np.nan==np.nan这种形式来作为判断条件,结果永远是False,==是用作值判断的,而NaN并没有值,如果你不想使用上方的判断方法,你可以使用is作为判断方法,is对象引用判断,np.nan is np.nan,结果就是你要的True。

    Series自动对齐:

    1. In [72]: data1
    2. Out[72]:
    3. a 1
    4. asd 1
    5. b 1
    6. dtype: int64
    7. In [73]: data
    8. Out[73]:
    9. a 1
    10. b 2
    11. c 3
    12. d 4
    13. dtype: int64
    14. In [74]: data+data1
    15. Out[74]:
    16. a 2.0
    17. asd NaN
    18. b 3.0
    19. c NaN
    20. d NaN
    21. dtype: float64

    从上面两个Series中不难看出各自的index所处位置并不完全相同,这时Series的自动对齐特性就发挥作用了,在算术运算中,Series会自动寻找匹配的index值进行运算,如果index不存在匹配则自动赋予NaN,值得注意的是,任何数+NaN=NaN,你可以把NaN理解为吸收一切的黑洞。

    Series的name属性:

    1. In [84]: data.index.name = 'abc'
    2. In [85]: data.name = 'test'
    3. In [86]: data
    4. Out[86]:
    5. abc
    6. a 1
    7. b 2
    8. c 3
    9. d 4
    10. Name: test, dtype: int64

    Series对象本身及其索引index都有一个name属性,name属性主要发挥作用是在DataFrame中,当我们把一个Series对象放进DataFrame中,新的列将根据我们的name属性对该列进行命名,如果我们没有给Series命名,DataFrame则会自动帮我们命名为0

    5.DataFrame

    DataFrame的生成:

    1. In [87]: data = {'name': ['BTC', 'ETH', 'EOS'], 'price':[50000, 4000, 150]}
    2. In [88]: data = DataFrame(data)
    3. In [89]: data
    4. Out[89]:
    5. name price
    6. 0 BTC 50000
    7. 1 ETH 4000
    8. 2 EOS 150

    DataFrame的生成与Series差不多,你可以自己指定index,也可不指定,DataFrame会自动帮你补上。

    查看DataFrame的相关信息:

    1. In [95]: data.index
    2. Out[95]: RangeIndex(start=0, stop=3, step=1)
    3. In [96]: data.values
    4. Out[96]:
    5. array([['BTC', 50000],
    6. ['ETH', 4000],
    7. ['EOS', 150]], dtype=object)
    8. In [97]: data.columns #DataFrame的列标签
    9. Out[97]: Index(['name', 'price'], dtype='object')

    DataFrame的索引:

    1. In [92]: data.name
    2. Out[92]:
    3. 0 BTC
    4. 1 ETH
    5. 2 EOS
    6. Name: name, dtype: object
    7. In [93]: data['name']
    8. Out[93]:
    9. 0 BTC
    10. 1 ETH
    11. 2 EOS
    12. Name: name, dtype: object
    13. In [94]: data.iloc[1] #loc['name']查询的是行标签
    14. Out[94]:
    15. name ETH
    16. price 4000
    17. Name: 1, dtype: object

    其实行索引,除了iloc,loc还有个ixix既可以进行行标签索引,也可以进行行号索引,但这也大大增加了它的不确定性,有时会出现一些奇怪的问题,所以pandas在0.20.0版本的时候就把ix给弃用了。

    6.DataFrame的常用操作

    简单地增加行、列:

    1. In [105]: data['type'] = 'token' #增加列
    2. In [106]: data
    3. Out[106]:
    4. name price type
    5. 0 BTC 50000 token
    6. 1 ETH 4000 token
    7. 2 EOS 150 token
    8. In [109]: data.loc['3'] = ['ae',200,'token'] #增加行
    9. In [110]: data
    10. Out[110]:
    11. name price type
    12. 0 BTC 50000 token
    13. 1 ETH 4000 token
    14. 2 EOS 150 token
    15. 3 ae 200 token

    删除行、列操作:

    1. In [117]: del data['type'] #删除列
    2. In [118]: data
    3. Out[118]:
    4. name price
    5. 0 BTC 50000
    6. 1 ETH 4000
    7. 2 EOS 150
    8. 3 ae 200
    9. In [120]: data.drop([2]) #删除行
    10. Out[120]:
    11. name price
    12. 0 BTC 50000
    13. 1 ETH 4000
    14. 3 ae 200
    15. In [121]: data
    16. Out[121]:
    17. name price
    18. 0 BTC 50000
    19. 1 ETH 4000
    20. 2 EOS 150
    21. 3 ae 200

    这里需要注意的是,使用drop()方法返回的是Copy而不是视图,要想真正在原数据里删除行,就要设置inplace=True

    1. In [125]: data.drop([2],inplace=True)
    2. In [126]: data
    3. Out[126]:
    4. name price
    5. 0 BTC 50000
    6. 1 ETH 4000
    7. 3 ae 200

    设置某一列为index:

    1. In [131]: data.set_index(['name'],inplace=True)
    2. In [132]: data
    3. Out[132]:
    4. price
    5. name
    6. BTC 50000
    7. ETH 4000
    8. ae 200
    9. In [133]: data.reset_index(inplace=True) #将index返回回dataframe中
    10. In [134]: data
    11. Out[134]:
    12. name price
    13. 0 BTC 50000
    14. 1 ETH 4000
    15. 2 ae 200

    处理缺失值:

    1. In [149]: data
    2. Out[149]:
    3. name price
    4. 0 BTC 50000.0
    5. 1 ETH 4000.0
    6. 2 ae 200.0
    7. 3 eos NaN
    8. In [150]: data.dropna() #丢弃含有缺失值的行
    9. Out[150]:
    10. name price
    11. 0 BTC 50000.0
    12. 1 ETH 4000.0
    13. 2 ae 200.0
    14. In [151]: data.fillna(0) #填充缺失值数据为0
    15. Out[151]:
    16. name price
    17. 0 BTC 50000.0
    18. 1 ETH 4000.0
    19. 2 ae 200.0
    20. 3 eos 0.0

    还是需要注意:这些方法返回的是copy而不是视图,如果想在原数据上改变,别忘了inplace=True

    数据合并:

    1. In [160]: data
    2. Out[160]:
    3. name price
    4. 0 BTC 50000.0
    5. 1 ETH 4000.0
    6. 2 ae 200.0
    7. 3 eos NaN
    8. In [161]: data1
    9. Out[161]:
    10. name other
    11. 0 BTC 50000
    12. 1 BTC 4000
    13. 2 EOS 150
    14. In [162]: pd.merge(data,data1,on='name',how='left') #以name为key进行左连接
    15. Out[162]:
    16. name price other
    17. 0 BTC 50000.0 50000.0
    18. 1 BTC 50000.0 4000.0
    19. 2 ETH 4000.0 NaN
    20. 3 ae 200.0 NaN
    21. 4 eos NaN NaN

    平时进行数据合并操作,更多的会出一种情况,那就是出现重复值,DataFrame也为我们提供了简便的方法:

    data.drop_duplicates(inplace=True)

    数据的简单保存与读取:

    1. In [165]: data.to_csv('test.csv')
    2. In [166]: pd.read_csv('test.csv')
    3. Out[166]:
    4. Unnamed: 0 name price
    5. 0 0 BTC 50000.0
    6. 1 1 ETH 4000.0
    7. 2 2 ae 200.0
    8. 3 3 eos NaN

    为什么会出现这种情况呢,从头看到尾的同学可能就看出来了,增加第三行时,我用的是loc[‘3’]行标签来增加的,而read_csv方法是默认index是从0开始增长的,此时只需要我们设置下index参数就ok了:

    1. In [167]: data.to_csv('test.csv',index=None) #不保存行索引
    2. In [168]: pd.read_csv('test.csv')
    3. Out[168]:
    4. name price
    5. 0 BTC 50000.0
    6. 1 ETH 4000.0
    7. 2 ae 200.0
    8. 3 eos NaN

    其他的还有header参数, 这些参数都是我们在保存数据时需要注意的。

  • 相关阅读:
    POSTGRESQL 从越来越多的ORACLE DBA 考取 PG 证书, 回顾2019- 2022
    黑客(网络安全)技术自学30天
    Python中关于文件的操作,一篇就够了
    QDockWidget DEMO 动态添加QDockWidget ,无主窗口,禁止tab重叠
    Go语言基础
    EasyCVR视频汇聚平台云计算技术核心优势:高效、灵活与可扩展性深度解读
    华为防火墙基础自学系列 | IPsec技术详解
    使用DIV+CSS技术设计的非遗文化网页与实现制作(web前端网页制作课作业)
    软件测试面试题:智力题。
    PM2管理器无法使用解决方法
  • 原文地址:https://blog.csdn.net/weixin_45925028/article/details/132856985