《Python数据科学手册》抄书笔记,第三章: Pandas数据处理

Pandas是在NumPy基础上建立的新程序库,提供了一种高效的DataFrame数据结构。DataFrame本质上是一种带行标签和列标签、支持相同类型数据和缺失值的多维数组。

 

3.1 安装并使用pandas

import pandas

pandas.__version__

'1.0.5'

 一般会简写成pd

import pandas as pd

 

3.2Pandas对象介绍

如果从底层视角观察Pandas对象,可以把它们看成增强版的NumPy结构化数组,行列都不再只是简单的整数索引,还可以带上便签。

Pandas的三个基本数据结构:Series, DateFrame,Index

先从倒包开始:

import numpy as np
import pandas as pd 

 

3.2.1 Pandas的series对象

Pandas的Series对象是一个带索引数据构成的一维数组。可以用一个数组创建Series对象,如下所示:

data = pd.Series(np.linspace(0.25,1,4))

data

0    0.25
1    0.50
2    0.75
3    1.00
dtype: float64

 从输出可以看到Series对象将一组数据和一组索引绑定在一起,可以从对象的values和index获取相关属性。

data.values

array([0.25, 0.5 , 0.75, 1.  ])

 

data.index

RangeIndex(start=0, stop=4, step=1)

 

和Numpy'数组一样,数据可以通过Python的中括号索引标签获取:

data[1]

0.5

data[1:3]

1    0.50
2    0.75
dtype: float64

 

1.Series是通用的Numpy数组

你可能会觉得Series对象和一维NumPy数组基本可以等价交换,但两者的本质差异其实是索引:

NumPy数组通过隐式定义的整数获取索引数值,而Pandas对象用一种显式定义的索引与数值关联。

注意一个隐式,一个是显式

显式索引让Series的索引不仅仅是整数,还可以是任意想要的类型。

data = pd.Series([0.25,0.5,0.75,1.0],index=list('abcd'))

data

a    0.25
b    0.50
c    0.75
d    1.00
dtype: float64

 取值还是通过索引取值

data['b']

0.5

 也可以使用不连续索引:

data = pd.Series([0.25,0.5,0.75,1.0],index=[2,5,3,7])

data

2    0.25
5    0.50
3    0.75
7    1.00
dtype: float64

data[5]

0.5

 

2.Series是特殊的字典

我们可以把Pandas的Series对象看成一种特殊的Python字典。字典是这一种将任意键映射到一组任意值的数据结构,而Series对象其实是一种将类型键映射到一组类型值的数据结构。可以直接用Python的字典创建一个Series对象。、

population_dict = {'California': 38332521,
                   'Texas': 26448193,
                   'New York': 19651127,
                   'Florida': 19552860,
                   'Illinois': 12882135}
population = pd.Series(population_dict)
population

 输出

California    38332521
Texas         26448193
New York      19651127
Florida       19552860
Illinois      12882135
dtype: int64

 用字典创建Series对象时,其索引默认按照顺序排序。取值还是通过索引取值

population['California']

38332521

 与字典不同,Series还支持切片操作

population['California':'Illinois']

California    38332521
Texas         26448193
New York      19651127
Florida       19552860
Illinois      12882135
dtype: int64

 

创建Series对象

前面已经看到过的创建方式:

pd.Series(data, index=index)

 index是一个可选参数,data参数支持多种数据类型。

pd.Series([2, 4, 6])

0    2
1    4
2    6
dtype: int64

 上面演示了默认的index,data也可以是标量,索引后面会自动填充

pd.Series(5, index=[100,200,300])

100    5
200    5
300    5
dtype: int64

 data还可以是一个字典,index默认是排序的字典key

pd.Series({2:'a', 1:'b', 3:'c'})

2    a
1    b
3    c
dtype: object

 每一种形式可以通过显式指定索引需要的结果:

pd.Series({2:'a',1:'b',3:'c'},index=[3,2])

3    c
2    a
dtype: object

 筛选出来的将不在排序,而且需要注意的是,Series对象只会保留显式定义的键值对。

 

3.2.2Pandas的DataFrame对象

同样DataFrame既可以作为一个通用型NumPy数组,也可以看做特殊的Python字典。

先再来创建另外一个Series对象:

area_dict = {'California': 423967, 'Texas': 695662, 'New York': 141297,
             'Florida': 170312, 'Illinois': 149995}
area = pd.Series(area_dict)
area

 

California    423967
Texas         695662
New York      141297
Florida       170312
Illinois      149995
dtype: int64

 与之前创建population的Series对象一样,用一个字典创建这些二维对象。

states = pd.DataFrame({'populatioan':population,
                      'area': area})
states

 输出

 	populatioan 	area
California 	38332521 	423967
Texas 	26448193 	695662
New York 	19651127 	141297
Florida 	19552860 	170312
Illinois 	12882135 	149995

 DataFrame也有index属性

states.index

Index(['California', 'Texas', 'New York', 'Florida', 'Illinois'], dtype='object')

 DataFrame还有一个columns属性,是存放标签的index对象

states.columns

Index(['populatioan', 'area'], dtype='object')

 因此DateFrame可以看作一种通用的NumPy二维数组,它的行与列都可以通过索引获取

states['area']

 

California    423967
Texas         695662
New York      141297
Florida       170312
Illinois      149995
Name: area, dtype: int64

 通过索引可以获取一个列数据,也就是一个Series

3.创建DataFrame对象

1通过Series对象创建。DataFrame是一组Series对象的集合,可以用单个Series创建一个单列的DateFrame

pd.DataFrame(population,columns=['population'])

 输出

 	population
California 	38332521
Texas 	26448193
New York 	19651127
Florida 	19552860
Illinois 	12882135

 2通过字典的列表创建。任何只要是列表套字典的形式就可以转换成DataFrame,字典的key是DateFrame的columns名称。

data =[{'a':i,'b':2*i} 
      for i in range(3)]
pd.DataFrame(data)

 输出

a 	b
0 	0 	0
1 	1 	2
2 	2 	4

 即使有些键不存在,Pandas也会用缺失值Nan

pd.DataFrame([{'a': 1, 'b': 2}, {'b': 3, 'c': 4}])

 输出

a 	b 	c
0 	1.0 	2 	NaN
1 	NaN 	3 	4.0

 

通过Series对象字典创建,跟前面的示例一样

pd.DataFrame({'polulation':population,
             'area': area})

 输出

polulation 	area
California 	38332521 	423967
Texas 	26448193 	695662
New York 	19651127 	141297
Florida 	19552860 	170312
Illinois 	12882135 	149995

 

通过二维数组创建,如果如果不指定行与列的索引,那么行列默认都是整数索引值

pd.DataFrame(np.random.rand(3,2),
            columns=['foo','bar'],
            index=['a','b','c'])

 输出

foo 	bar
a 	0.926270 	0.753726
b 	0.537491 	0.967508
c 	0.817875 	0.590719

 

最后书中的说明,是将结构化数组直接转换

A=np.zeros(3,dtype=({'names':('A','B'),'formats':('i8','f8')}))

A

array([(0, 0.), (0, 0.), (0, 0.)], dtype=[('A', '<i8'), ('B', '<f8')])

pd.DataFrame(A)

	A 	B
0 	0 	0.0
1 	0 	0.0
2 	0 	0.0

 

3.2.3Pandas的Index对象

Pandas的index是一个不可变的数组,同时也是一个有序的集合。

首先创建一个Index对象:

ind = pd.Index([2,3,5,7,11])

ind

Int64Index([2, 3, 5, 7, 11], dtype='int64')

 

1将index看做不可变的数组

尽然这么说了,除了赋值,另外的操作应该都可以

ind[1]

3

ind[::2]

Int64Index([2, 5, 11], dtype='int64')

Index objects also have many of the attributes familiar from NumPy arrays:

print(ind.size, ind.shape, ind.ndim, ind.dtype)

5 (5,) 1 int64

 可以切片,可以通过索引取值,可以输出size,shape等属性。

但通过索引赋值就报错。

Index对象的不可变特性,使得多个DateFrame和数组之间进行索引共享时更加安全,尤其是可以避免修改索引时粗心大意而导致的副作用。

 

将Index看作有序集合

indA = pd.Index([1, 3, 5, 7, 9])

indB = pd.Index([2, 3, 5, 7, 11])

indA & indB  # intersection

Int64Index([3, 5, 7], dtype='int64')

indA | indB  # union

Int64Index([1, 2, 3, 5, 7, 9, 11], dtype='int64')

indA ^ indB  # symmetric difference

Int64Index([1, 2, 9, 11], dtype='int64')

 与Python的集合操作一样,分别操作了并集,交集,异或

 


































 

3.3数据取值与选择

3.3.1Series数据选择的方法

把Series对象与NumPy数组和Python字典在许多方面一样,记住这个类比,可以让我们更好的理解Series对象的数据索引与选择模式

1.将Series看作字典

 
import pandas as pd
import numpy as np
data = pd.Series(np.linspace(0.25,1,4),
                index=list('abcd'))
data
a    0.25
b    0.50
c    0.75
d    1.00
dtype: float64
 
data['b']
0.5

还可以用Python字典的一些方法

 
 
'a' in data
True
 
 
data.keys()
Index(['a', 'b', 'c', 'd'], dtype='object')
 
list(data.items())
[('area',
  California    423967
  Texas         695662
  New York      141297
  Florida       170312
  Illinois      149995
  Name: area, dtype: int64),
 ('pop',
  California    38332521
  Texas         26448193
  New York      19651127
  Florida       19552860
  Illinois      12882135
  Name: pop, dtype: int64)]

通过添加索引值,理解为字典添加key,来扩展Series

 
 
data['e'] = 1.25
 
 
data
 areapope
California 423967 38332521 1.25
Texas 695662 26448193 1.25
New York 141297 19651127 1.25
Florida 170312 19552860 1.25
Illinois 149995 12882135 1.25

2将Series看作一维数组

Series不仅有着和字典一样的接口,而且还具备和NumPy数组一样的数组数据选择功能,包括索引、掩码、花哨的索引等操作

 
 
data
 areapope
California 423967 38332521 1.25
Texas 695662 26448193 1.25
New York 141297 19651127 1.25
Florida 170312 19552860 1.25
Illinois 149995 12882135 1.25
 
 
# 将显式索引作为切片
data['a':'c']
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
~/opt/anaconda3/lib/python3.8/site-packages/pandas/core/indexes/base.py in get_slice_bound(self, label, side, kind)
   4844             try:
-> 4845                 return self._searchsorted_monotonic(label, side)
   4846             except ValueError:

~/opt/anaconda3/lib/python3.8/site-packages/pandas/core/indexes/base.py in _searchsorted_monotonic(self, label, side)
   4805 
-> 4806         raise ValueError("index must be monotonic increasing or decreasing")
   4807 

ValueError: index must be monotonic increasing or decreasing

During handling of the above exception, another exception occurred:

KeyError                                  Traceback (most recent call last)
<ipython-input-110-eccbdf2cf843> in <module>
      1 # 将显式索引作为切片
----> 2 data['a':'c']

~/opt/anaconda3/lib/python3.8/site-packages/pandas/core/frame.py in __getitem__(self, key)
   2777 
   2778         # Do we have a slicer (on rows)?
-> 2779         indexer = convert_to_index_sliceable(self, key)
   2780         if indexer is not None:
   2781             # either we have a slice or we have a string that can be converted

~/opt/anaconda3/lib/python3.8/site-packages/pandas/core/indexing.py in convert_to_index_sliceable(obj, key)
   2265     idx = obj.index
   2266     if isinstance(key, slice):
-> 2267         return idx._convert_slice_indexer(key, kind="getitem")
   2268 
   2269     elif isinstance(key, str):

~/opt/anaconda3/lib/python3.8/site-packages/pandas/core/indexes/base.py in _convert_slice_indexer(self, key, kind)
   2961             indexer = key
   2962         else:
-> 2963             indexer = self.slice_indexer(start, stop, step, kind=kind)
   2964 
   2965         return indexer

~/opt/anaconda3/lib/python3.8/site-packages/pandas/core/indexes/base.py in slice_indexer(self, start, end, step, kind)
   4711         slice(1, 3)
   4712         """
-> 4713         start_slice, end_slice = self.slice_locs(start, end, step=step, kind=kind)
   4714 
   4715         # return a slice

~/opt/anaconda3/lib/python3.8/site-packages/pandas/core/indexes/base.py in slice_locs(self, start, end, step, kind)
   4924         start_slice = None
   4925         if start is not None:
-> 4926             start_slice = self.get_slice_bound(start, "left", kind)
   4927         if start_slice is None:
   4928             start_slice = 0

~/opt/anaconda3/lib/python3.8/site-packages/pandas/core/indexes/base.py in get_slice_bound(self, label, side, kind)
   4846             except ValueError:
   4847                 # raise the original KeyError
-> 4848                 raise err
   4849 
   4850         if isinstance(slc, np.ndarray):

~/opt/anaconda3/lib/python3.8/site-packages/pandas/core/indexes/base.py in get_slice_bound(self, label, side, kind)
   4840         # we need to look up the label
   4841         try:
-> 4842             slc = self.get_loc(label)
   4843         except KeyError as err:
   4844             try:

~/opt/anaconda3/lib/python3.8/site-packages/pandas/core/indexes/base.py in get_loc(self, key, method, tolerance)
   2646                 return self._engine.get_loc(key)
   2647             except KeyError:
-> 2648                 return self._engine.get_loc(self._maybe_cast_indexer(key))
   2649         indexer = self.get_indexer([key], method=method, tolerance=tolerance)
   2650         if indexer.ndim > 1 or indexer.size > 1:

pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc()

pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc()

pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item()

pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item()

KeyError: 'a'

 
 
# 将数字整数索引作为切片
data[0:2]
 
# 掩码取值
data[(data>0.3)&(data<0.8)]
 
 
# 花哨取值
data[['a','e']]

切片索引记住,用显式的时候时取头取尾的,用隐式的时候时取头不取尾的

3.索引器:loc、iloc和ix

如果是显式的整数索引,就会与隐式的让你迷惑

 
data = pd.Series(list('abc'),
                 index=[1,3,5])
data
 
 
# 索引取值是显式的
data[1]
 
# 切片用的是隐式的
data[1:3]

由于整数索引的混淆,Pandas提供了索引器(indexer)属性来取值的方法。它们不是Series对象的函数方法,而是暴露切片接口的属性

 
 
# loc显式索引的使用
data.loc[1]
 
 
data.loc[1:3]
 
 
# iloc隐式索引的使用
 
 
data.iloc[1]
 
 
data.iloc[1:3]
 
pd.__version__
 
 
pd.ix

在Pandas1.0.5,Series对象已经没有ix属性了

3.3.2DataFrame数据选择方法

DateFrame有些方面像二维或结构化数组,在有些方面又像共享索引的若干Series对象构成的字典

将DataFrame看作字典

先创建两个Series对象

 
area = pd.Series({'California': 423967, 'Texas': 695662,
                  'New York': 141297, 'Florida': 170312,
                  'Illinois': 149995})
pop = pd.Series({'California': 38332521, 'Texas': 26448193,
                 'New York': 19651127, 'Florida': 19552860,
                 'Illinois': 12882135})
 
# 通过Series创建DataFrame
data = pd.DataFrame({'area':area,'pop':pop})
data
 areapop
California 423967 38332521
Texas 695662 26448193
New York 141297 19651127
Florida 170312 19552860
Illinois 149995 12882135

获取列的数据可以通过字典方式[key]也可以通过.attr的方式获取

 
data['area']
California    423967
Texas         695662
New York      141297
Florida       170312
Illinois      149995
Name: area, dtype: int64
 
data.area
California    423967
Texas         695662
New York      141297
Florida       170312
Illinois      149995
Name: area, dtype: int64
 
data['area'] is data.area
True

可以看到取出的对象是相同的

如果列名不是存字符形式,或者与DateFrame的方法名重名就不行

 
data.pop is data['pop']
False
 
data.pop
<bound method NDFrame.pop of               area       pop
California  423967  38332521
Texas       695662  26448193
New York    141297  19651127
Florida     170312  19552860
Illinois    149995  12882135>
 
data['pop']
California    38332521
Texas         26448193
New York      19651127
Florida       19552860
Illinois      12882135
Name: pop, dtype: int64

和前面的Series添加索引类似,DateFrame添加columns也非常方便,通过增加key的方式就可以了

 
data['density'] = data['pop'] / data.area
data
 areapopdensity
California 423967 38332521 90.413926
Texas 695662 26448193 38.018740
New York 141297 19651127 139.076746
Florida 170312 19552860 114.806121
Illinois 149995 12882135 85.883763

2.将DateFrame看作二维数组

 
data
 areapopdensity
California 423967 38332521 90.413926
Texas 695662 26448193 38.018740
New York 141297 19651127 139.076746
Florida 170312 19552860 114.806121
Illinois 149995 12882135 85.883763
 
# DataFrame的values属性查看数据
data.values
array([[4.23967000e+05, 3.83325210e+07, 9.04139261e+01],
       [6.95662000e+05, 2.64481930e+07, 3.80187404e+01],
       [1.41297000e+05, 1.96511270e+07, 1.39076746e+02],
       [1.70312000e+05, 1.95528600e+07, 1.14806121e+02],
       [1.49995000e+05, 1.28821350e+07, 8.58837628e+01]])
 
# 进行转置
data.T
 CaliforniaTexasNew YorkFloridaIllinois
area 4.239670e+05 6.956620e+05 1.412970e+05 1.703120e+05 1.499950e+05
pop 3.833252e+07 2.644819e+07 1.965113e+07 1.955286e+07 1.288214e+07
density 9.041393e+01 3.801874e+01 1.390767e+02 1.148061e+02 8.588376e+01
 
# 获取行信息,通过values属性来
data.values[0]
array([4.23967000e+05, 3.83325210e+07, 9.04139261e+01])
 
# 获取列信息,直接通过列索引就可以了
data.area
California    423967
Texas         695662
New York      141297
Florida       170312
Illinois      149995
Name: area, dtype: int64

当然也可以通过loc【显式】或iloc【隐式】,进行行与列的选择

 
# 用隐式的方式取三行两列
data.iloc[:3, :2]
 areapop
California 423967 38332521
Texas 695662 26448193
New York 141297 19651127
 
# 用显式的方式取三行两列,记住这个是取头取尾的
data.loc[:'New York', :'pop']
 areapop
California 423967 38332521
Texas 695662 26448193
New York 141297 19651127

任何用于处理NumPy形式数据的方法都可以用于这些索引器

 
# 结合掩码与花哨索引取值
data.loc[data.density>100, ['pop','density']]
 popdensity
New York 19651127 139.076746
Florida 19552860 114.806121
 
# 赋值操作
data.iloc[0,2] =90
data
 areapopdensity
California 423967 38332521 90.000000
Texas 695662 26448193 38.018740
New York 141297 19651127 139.076746
Florida 170312 19552860 114.806121
Illinois 149995 12882135 85.883763

如果对单个标签取值就是选择列,而对多个便签进行切片就选择行

 
data['Florida':'Illinois']
 areapopdensity
Florida 170312 19552860 114.806121
Illinois 149995 12882135 85.883763
 
# 直接通过数字索引取值
data[-2:]
 areapopdensity
Florida 170312 19552860 114.806121
Illinois 149995 12882135 85.883763
 
# 直接通过列数据的筛选,不是要是loc索引器
data[data.density >100]
 areapopdensity
New York 141297 19651127 139.076746
Florida 170312 19552860 114.806121
 
data.loc[data.density > 100]
 areapopdensity
New York 141297 19651127 139.076746
Florida 170312 19552860 114.806121
 
n = data.density > 100
n
California    False
Texas         False
New York       True
Florida        True
Illinois      False
Name: density, dtype: bool




# 3.4 Pandas数值运算方法

Pandas对于一元运算(像函数与三角函数),这些通用函数将在输出结果中保留索引和列标签;
而对于二元运算(如加法和乘法),Pandas在传递通用函数时会自动对齐索引进行计算。

## 3.4.1通用函数:保留索引

因为Pandas是建立在NumPy基础之上的,所以NumPy的通用函数同样适用于Pandas的Series与DataFrame对象

import pandas as pd
import numpy as np

rng = np.random.RandomState(42)
ser = pd.Series(rng.randint(0,10,4))
ser

df = pd.DataFrame(rng.randint(0, 10, (3, 4)),
                  columns=['A', 'B', 'C', 'D'],
                 index=['one','two','three'])
df

np.exp(ser)

np.sin(df * np.pi /4)

## 3.4.2通用函数:索引对齐

Pandas会在计算过程中对齐两个对象的索引

### 1.Series索引对齐

area = pd.Series({'Alaska': 1723337, 'Texas': 695662,
                  'California': 423967}, name='area')
population = pd.Series({'California': 38332521, 'Texas': 26448193,
                        'New York': 19651127}, name='population')
population

# 人口除以面积
population / area

返回的对象的索引是输入的对象的索引的并集

area.index | population.index

缺失的位置用NAN来显式

A = pd.Series([2, 4, 6], index=[0, 1, 2])
B = pd.Series([1, 3, 5], index=[1, 2, 3])
A + B

对于缺失的数值,你可以填写合适的值进行运算,可以通过.add的方法添加

A.add(B, fill_value=0)

### 2.DateFrame索引对齐

在计算两个DataFrame时,类似的索引对齐规则也同样会出现在共同(并集)列中

A = pd.DataFrame(rng.randint(0,20,size=(2,2)),
                columns=list('AB'))
A

B = pd.DataFrame(rng.randint(0,10,size=(3,3)),
                columns=list('BAC'))
B

 A+B

A

同样也可以通过fill_valuel的方法,来自定义缺省值

print(A.stack().__class__)
A.stack()

stack的方法可以将DateFrame对象转换成一维的Series对象

fill = A.stack().mean()
A.add(B, fill_value=fill)

下面展示了Python运算符与Pandas方法的映射关系

| Python Operator | Pandas Method(s)                      |
|-----------------|---------------------------------------|
| ``+``           | ``add()``                             |
| ``-``           | ``sub()``, ``subtract()``             |
| ``*``           | ``mul()``, ``multiply()``             |
| ``/``           | ``truediv()``, ``div()``, ``divide()``|
| ``//``          | ``floordiv()``                        |
| ``%``           | ``mod()``                             |
| ``**``          | ``pow()``                             |

## 3.4.3 通用函数: DateFrame与Series的运算

DateFrame和Series的运算规则,与NumPy中二维数组与一维数组的运算规则是一样的

A = rng.randint(10, size=(3,4))
A

A - A[0]

这个是根据的NumPy的广播机制

df = pd.DataFrame(A,columns=list('QRST'))
df - df.iloc[0]

如果想案例列进行计算,则可以通过设置axis参数

df.sub(df['R'],axis=0)

DateFrame/Series的运算,结果会自动对齐索引

half = df.iloc[0,::2]
half

df -half

因为half只有QS的列选择,所以只对该两列进行了操作。

 

 

 

posted @ 2020-10-16 17:17  就是想学习  阅读(332)  评论(0编辑  收藏  举报