Pandas
引入
前面一篇文章我们介绍了 numpy, 但 numpy 的特长并不是在于数据处理, 而是在它能非常方便地实现科学计算, 所以我们日常对数据进行处理时用的 numpy 情况并不是很多, 我们需要处理的数据一般都是带有列标签和 index 索引的, 而 numpy 并不支持这些, 这时我们就需要 pandas 上场啦!
WHAT?
Pandas 是基于 Numpy 构建的库, 在数据处理方面可以把它理解为 numpy 加强版, 同时 Pandas 也是一项开源项目 . 不同于 numpy 的是, pandas 拥有种数据结构: Series 和 DataFrame:
下面我们就来生成一个简单的 series 对象来方便理解:
- In [1]: from pandas import Series,DataFrame
- In [2]: import pandas as pd
- In [3]: data = Series([1,2,3,4],index = ['a','b','c','d'])
- In [4]: data
- Out[4]:
- a 1
- b 2
- c 3
- d 4
- dtype: int64
Series 是一种类似一维数组的数据结构, 由一组数据和与之相关的 index 组成, 这个结构一看似乎与 dict 字典差不多, 我们知道字典是一种无序的数据结构, 而 pandas 中的 Series 的数据结构不一样, 它相当于定长有序的字典, 并且它的 index 和 value 之间是独立的, 两者的索引还是有区别的, Series 的 index 是可变的, 而 dict 字典的 key 值是不可变的.
下面照例生成一个简单的 DataFrame 对象:
- In [8]: data = {'a':[1,2,3],'b':['we','you','they'],'c':['btc','eos','ae']}
- In [9]: df = DataFrame(data)
- In [10]: df
- Out[10]:
- a b c
- 0 1 we btc
- 1 2 you eos
- 2 3 they ae
DataFrame 这种数据结构我们可以把它看作是一张二维表, DataFrame 长得跟我们平时使用的 Excel 表格差不多, DataFrame 的横行称为 columns, 竖列和 Series 一样称为 index,DataFrame 每一列可以是不同类型的值集合, 所以 DataFrame 你也可以把它视为不同数据类型同一 index 的 Series 集合.
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 搭配, 不用复杂的代码, 就可以生成多种多样的数据视图.
HOW?
Series
Series 的两种生成方式:
- In [19]: data = Series([222,'btc',234,'eos'])
- In [20]: data
- Out[20]:
- 0 222
- 1 btc
- 2 234
- 3 eos
- dtype: object
虽然我们在生成的时候没有设置 index 值, 但 Series 还是会自动帮我们生成 index, 这种方式生成的 Series 结构跟 list 列表差不多, 可以把这种形式的 Series 理解为竖起来的 list 列表.
- In [21]: data = Series([1,2,3,4],index = ['a','b','c','d'])
- In [22]: data
- Out[22]:
- a 1
- b 2
- c 3
- d 4
- dtype: int64
这种形式的 Series 可以理解为 numpy 的 array 外面披了一件 index 的马甲, 所以 array 的相关操作, Series 同样也是支持的. 结构非常相似的 dict 字典同样也是可以转化为 Series 格式的:
- In [29]: dic = {
- 'a':1,'b':2,'c':'as'
- }
- In [30]: dicSeries = Series(dic)
查看 Series 的相关信息:
- In [32]: data.index
- Out[32]: Index(['a', 'b', 'c', 'd'], dtype='object')
- In [33]: data.values
- Out[33]: array([1, 2, 3, 4], dtype=int64)
- In [35]: 'a' in data #in 方法默认判断的是 index 值
- Out[35]: True
Series 的 NaN 生成:
- In [46]: index1 = [ 'a','b','c','d']
- In [47]: dic = {
- 'b':1,'c':1,'d':1
- }
- In [48]: data2 = Series(dic,index=index1)
- In [49]: data2
- Out[49]:
- a NaN
- b 1.0
- c 1.0
- d 1.0
- dtype: float64
从这里我们可以看出 Series 的生成依据的是 index 值, index'a'在字典 dic 的 key 中并不存在, Series 自然也找不到'a'的对应 value 值, 这种情况下 Pandas 就会自动生成 NaN(not a number) 来填补缺失值, 这里还有个有趣的现象, 原本 dtype 是 int 类型, 生成 NaN 后就变成了 float 类型了, 因为 NaN 的官方定义就是 float 类型.
NaN 的相关查询:
- In [58]: data2.isnull()
- Out[58]:
- a True
- b False
- c False
- d False
- dtype: bool
- In [59]: data2.notnull()
- Out[59]:
- a False
- b True
- c True
- d True
- dtype: bool
- In [60]: data2[data2.isnull()==True] #嵌套查询 NaN
- Out[60]:
- a NaN
- dtype: float64
- In [64]: data2.count() #统计非 NaN 个数
- Out[64]: 3
切记切记, 查询 NaN 值切记不要使用 np.nan==np.nan 这种形式来作为判断条件, 结果永远是 False,== 是用作值判断的, 而 NaN 并没有值, 如果你不想使用上方的判断方法, 你可以使用 is 作为判断方法, is 是对象引用判断, np.nan is np.nan, 结果就是你要的 True.
Series 自动对齐:
- In [72]: data1
- Out[72]:
- a 1
- asd 1
- b 1
- dtype: int64
- In [73]: data
- Out[73]:
- a 1
- b 2
- c 3
- d 4
- dtype: int64
- In [74]: data+data1
- Out[74]:
- a 2.0
- asd NaN
- b 3.0
- c NaN
- d NaN
- dtype: float64
从上面两个 Series 中不难看出各自的 index 所处位置并不完全相同, 这时 Series 的自动对齐特性就发挥作用了, 在算术运算中, Series 会自动寻找匹配的 index 值进行运算, 如果 index 不存在匹配则自动赋予 NaN, 值得注意的是, 任何数 + NaN=NaN, 你可以把 NaN 理解为吸收一切的黑洞.
Series 的 name 属性:
- In [84]: data.index.name = 'abc'
- In [85]: data.name = 'test'
- In [86]: data
- Out[86]:
- abc
- a 1
- b 2
- c 3
- d 4
- Name: test, dtype: int64
Series 对象本身及其索引 index 都有一个 name 属性, name 属性主要发挥作用是在 DataFrame 中, 当我们把一个 Series 对象放进 DataFrame 中, 新的列将根据我们的 name 属性对该列进行命名, 如果我们没有给 Series 命名, DataFrame 则会自动帮我们命名为 0.
DataFrame
DataFrame 的生成:
- In [87]: data = {'name': ['BTC', 'ETH', 'EOS'], 'price':[50000, 4000, 150]}
- In [88]: data = DataFrame(data)
- In [89]: data
- Out[89]:
- name price
- 0 BTC 50000
- 1 ETH 4000
- 2 EOS 150
DataFrame 的生成与 Series 差不多, 你可以自己指定 index, 也可不指定, DataFrame 会自动帮你补上.
查看 DataFrame 的相关信息:
- In [95]: data.index
- Out[95]: RangeIndex(start=0, stop=3, step=1)
- In [96]: data.values
- Out[96]:
- array([['BTC', 50000],
- ['ETH', 4000],
- ['EOS', 150]], dtype=object)
- In [97]: data.columns #DataFrame 的列标签
- Out[97]: Index(['name', 'price'], dtype='object')
DataFrame 的索引:
- In [92]: data.name
- Out[92]:
- 0 BTC
- 1 ETH
- 2 EOS
- Name: name, dtype: object
- In [93]: data['name']
- Out[93]:
- 0 BTC
- 1 ETH
- 2 EOS
- Name: name, dtype: object
- In [94]: data.iloc[1] #loc['name'] 查询的是行标签
- Out[94]:
- name ETH
- price 4000
- Name: 1, dtype: object
其实行索引, 除了 iloc,loc 还有个 ix,ix 既可以进行行标签索引, 也可以进行行号索引, 但这也大大增加了它的不确定性, 有时会出现一些奇怪的问题, 所以 pandas 在 0.20.0 版本的时候就把 ix 给弃用了.
DataFrame 的常用操作
简单地增加行, 列:
- In [105]: data['type'] = 'token' #增加列
- In [106]: data
- Out[106]:
- name price type
- 0 BTC 50000 token
- 1 ETH 4000 token
- 2 EOS 150 token
- In [109]: data.loc['3'] = ['ae',200,'token'] #增加行
- In [110]: data
- Out[110]:
- name price type
- 0 BTC 50000 token
- 1 ETH 4000 token
- 2 EOS 150 token
- 3 ae 200 token
删除行, 列操作:
- In [117]: del data['type'] #删除列
- In [118]: data
- Out[118]:
- name price
- 0 BTC 50000
- 1 ETH 4000
- 2 EOS 150
- 3 ae 200
- In [120]: data.drop([2]) #删除行
- Out[120]:
- name price
- 0 BTC 50000
- 1 ETH 4000
- 3 ae 200
- In [121]: data
- Out[121]:
- name price
- 0 BTC 50000
- 1 ETH 4000
- 2 EOS 150
- 3 ae 200
这里需要注意的是, 使用 drop() 方法返回的是 Copy 而不是视图, 要想真正在原数据里删除行, 就要设置 inplace=True:
- In [125]: data.drop([2],inplace=True)
- In [126]: data
- Out[126]:
- name price
- 0 BTC 50000
- 1 ETH 4000
- 3 ae 200
设置某一列为 index:
- In [131]: data.set_index(['name'],inplace=True)
- In [132]: data
- Out[132]:
- price
- name
- BTC 50000
- ETH 4000
- ae 200
- In [133]: data.reset_index(inplace=True) #将 index 返回回 dataframe 中
- In [134]: data
- Out[134]:
- name price
- 0 BTC 50000
- 1 ETH 4000
- 2 ae 200
处理缺失值:
- In [149]: data
- Out[149]:
- name price
- 0 BTC 50000.0
- 1 ETH 4000.0
- 2 ae 200.0
- 3 eos NaN
- In [150]: data.dropna() #丢弃含有缺失值的行
- Out[150]:
- name price
- 0 BTC 50000.0
- 1 ETH 4000.0
- 2 ae 200.0
- In [151]: data.fillna(0) #填充缺失值数据为 0
- Out[151]:
- name price
- 0 BTC 50000.0
- 1 ETH 4000.0
- 2 ae 200.0
- 3 eos 0.0
还是需要注意: 这些方法返回的是 copy 而不是视图, 如果想在原数据上改变, 别忘了 inplace=True.
数据合并:
- In [160]: data
- Out[160]:
- name price
- 0 BTC 50000.0
- 1 ETH 4000.0
- 2 ae 200.0
- 3 eos NaN
- In [161]: data1
- Out[161]:
- name other
- 0 BTC 50000
- 1 BTC 4000
- 2 EOS 150
- In [162]: pd.merge(data,data1,on='name',how='left') #以 name 为 key 进行左连接
- Out[162]:
- name price other
- 0 BTC 50000.0 50000.0
- 1 BTC 50000.0 4000.0
- 2 ETH 4000.0 NaN
- 3 ae 200.0 NaN
- 4 eos NaN NaN
平时进行数据合并操作, 更多的会出一种情况, 那就是出现重复值, DataFrame 也为我们提供了简便的方法:
data.drop_duplicates(inplace=True)
数据的简单保存与读取:
- In [165]: data.to_csv('test.csv')
- In [166]: pd.read_csv('test.csv')
- Out[166]:
- Unnamed: 0 name price
- 0 0 BTC 50000.0
- 1 1 ETH 4000.0
- 2 2 ae 200.0
- 3 3 eos NaN
为什么会出现这种情况呢, 从头看到尾的同学可能就看出来了, 增加第三行时, 我用的是 loc['3'] 行标签来增加的, 而 read_csv 方法是默认 index 是从 0 开始增长的, 此时只需要我们设置下 index 参数就 ok 了:
- In [167]: data.to_csv('test.csv',index=None) #不保存行索引
- In [168]: pd.read_csv('test.csv')
- Out[168]:
- name price
- 0 BTC 50000.0
- 1 ETH 4000.0
- 2 ae 200.0
- 3 eos NaN
其他的还有 header 参数, 这些参数都是我们在保存数据时需要注意的.
来源: https://www.cnblogs.com/peng104/p/10398490.html