Python 数据处理:Pandas库的使用

简介: 最重要的一个功能是,它可以对不同索引的对象进行算术运算。在将对象相加时,如果存在不同的索引对,则结果的索引就是该索引对的并集。是一种类似于一维数组的对象,它由一组数据(各种 NumPy 数据类型)以及一组与之相关的数据标签(即索引)组成。每个索引都有一些方法和属性,它们可用于设置逻辑并回答有关该索引所包含的数据的常见问题。看成是一个定长的有序字典,因为它是索引值到数据值的一个映射。跟对应的 NumPy 数组方法相比,它们都是基于没有缺失数据的假设而构建的。对于带有重复值的索引,数据选取的行为将会有些不同。
✅作者简介:人工智能专业本科在读,喜欢计算机与编程,写博客记录自己的学习历程。
🍎个人主页: 小嗷犬的博客
🍊个人信条:为天地立心,为生民立命,为往圣继绝学,为万世开太平。
🥭本文内容:Python 数据处理:Pandas库的使用

Pandas 是基于 NumPy 数组构建的,特别是基于数组的函数和不使用 for 循环的数据处理。

虽然 Pandas 采用了大量的 NumPy 编码风格,但二者最大的不同是 Pandas 是专门为处理表格和混杂数据设计的。而 NumPy 更适合处理统一的数值数组数据。

1.Pandas 数据结构

要使用 Pandas,首先就得熟悉它的两个主要数据结构: SeriesDataFrame

1.1 Series

Series是一种类似于一维数组的对象,它由一组数据(各种 NumPy 数据类型)以及一组与之相关的数据标签(即索引)组成。仅由一组数据即可产生最简单的 Series
import pandas as pd
obj = pd.Series([4,7,-5,3])
print(obj)
Series的字符串表现形式为:索引在左边,值在右边。由于没有为数据指定索引,于是会自动创建一个 0到N-1(N为数据的长度)的整数型索引。可以通过 Seriesvaluesindex属性获取其数组表示形式和索引对象:
import pandas as pd
obj = pd.Series([4,7,-5,3])
print(obj.values)
print(obj.index)
print(list(obj.index))
也可以使用特定的索引:
import pandas as pd
obj2 = pd.Series([5,2,-3,1], index=['d', 'b', 'c', 'a'])
print(obj2)
可以通过索引的方式选取 Series中的单个或一组值:
import pandas as pd
obj2 = pd.Series([5,2,-3,1], index=['d', 'b', 'c', 'a'])
print(obj2['a'])
print(obj2[['a','c','b']])
使用 NumPy 函数或类似 NumPy 的运算(如根据布尔型数组进行过滤、标量乘法、应用数学函数等)都会保留索引值的链接:
import pandas as pd
obj2 = pd.Series([5,2,-3,1], index=['d', 'b', 'c', 'a'])
print(obj2 > 0)
print(obj2[obj2 > 0])
print(obj2 * 10)
还可以将 Series看成是一个定长的有序字典,因为它是索引值到数据值的一个映射。它可以用在许多原本需要字典参数的函数中:
import pandas as pd
obj2 = pd.Series([5,2,-3,1], index=['d', 'b', 'c', 'a'])
print('b' in obj2)
print('e' in obj2)
如果数据被存放在一个Python的字典中,也可以直接通过这个字典来创建 Series
import pandas as pd
sdata = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}
obj3 = pd.Series(sdata)
print(obj3)
如果只传入一个字典,则结果 Series中的索引就是原字典的键( 有序排列)。你可以传入排好序的字典的键以改变顺序:
# 在这个例子中,sdata中跟states索引相匹配的那3个值会被找出来并放到相应的位置上,
# 但由于 "California" 所对应的sdata值找不到,
# 所以其结果就为NaN(即“非数字”(Not a Number),在Pandas中,它用于表示缺失值或NA值)。
# 因为 "Utah" 不在states中,它被从结果中除去。

import pandas as pd
sdata = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}
states = ['California', 'Oregon', 'Texas', 'Ohio']
obj4 = pd.Series(sdata, index=states)
print(obj4)
Pandasisnullnotnull函数可用于检测缺失数据:
import pandas as pd
sdata = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}
states = ['California', 'Oregon', 'Texas', 'Ohio']
obj4 = pd.Series(sdata, index=states)
print(pd.isnull(obj4))
print(pd.notnull(obj4))
Series也有类似的实例方法:
import pandas as pd
sdata = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}
states = ['California', 'Oregon', 'Texas', 'Ohio']
obj4 = pd.Series(sdata, index=states)
print(obj4.isnull())
对于许多应用而言, Series最重要的一个功能是,它会根据运算的索引标签 自动对齐数据:
import pandas as pd
sdata = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}
obj3 = pd.Series(sdata)
states = ['California', 'Oregon', 'Texas', 'Ohio']
obj4 = pd.Series(sdata, index=states)

print(obj3)
print(obj4)
print(obj3 + obj4)
Series对象本身及其索引都有一个 name属性,该属性跟 Pandas 其他的关键功能关系非常密切:
import pandas as pd
sdata = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}
states = ['California', 'Oregon', 'Texas', 'Ohio']
obj4 = pd.Series(sdata, index=states)
obj4.name = 'population'
obj4.index.name = 'state'
print(obj4)

1.2 DataFrame

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

创建DataFrame的办法有很多,最常用的一种是直接传入一个由等长列表或 NumPy 数组组成的字典:

import pandas as pd
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)
print(frame)
结果 DataFrame会自动加上索引(跟 Series一样),且全部列会被有序排列。

对于特别大的DataFramehead方法会选取前五行:

import pandas as pd
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)
print(frame.head(2))
如果指定了列序列,则 DataFrame的列就会按照指定顺序进行排列:
import pandas as pd
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, columns=['year', 'state', 'pop'])
print(frame)
如果传入的列在数据中找不到,就会在结果中产生 缺失值
import pandas as pd
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]}
frame2 = pd.DataFrame(data, columns=['year', 'state', 'pop', 'debt'],
                      index=['one', 'two', 'three', 'four','five', 'six'])
print(frame2)
print(frame2.columns)
通过类似字典标记的方式或属性的方式,可以将 DataFrame的列获取为一个 Series
import pandas as pd
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]}
frame2 = pd.DataFrame(data, columns=['year', 'state', 'pop', 'debt'],
                      index=['one', 'two', 'three', 'four','five', 'six'])
print(frame2['state'])
print(frame2.year)
注意: 返回的 Series拥有原 DataFrame相同的索引,且其 name属性也已经被相应地设置好了。

也可以通过位置或名称的方式进行获取,比如用loc属性:

import pandas as pd
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]}
frame2 = pd.DataFrame(data, columns=['year', 'state', 'pop', 'debt'],
                      index=['one', 'two', 'three', 'four','five', 'six'])
frame2.loc['three']
列可以通过赋值的方式进行修改。例如,可以给那个空的 "debt"列赋上一个标量值或一组值:
import pandas as pd
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]}
frame2 = pd.DataFrame(data, columns=['year', 'state', 'pop', 'debt'],
                      index=['one', 'two', 'three', 'four','five', 'six'])
frame2['debt'] = 16.5
print(frame2)

import numpy as np
frame2['debt'] = np.arange(6.)
print(frame2)
将列表或数组赋值给某个列时, 其长度必须跟DataFrame的长度相匹配。如果赋值的是一个 Series,就会 精确匹配DataFrame的索引,所有的空位都将被填上缺失值:
import pandas as pd
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]}
frame2 = pd.DataFrame(data, columns=['year', 'state', 'pop', 'debt'],
                      index=['one', 'two', 'three', 'four','five', 'six'])
val = pd.Series([-1.2, -1.5, -1.7], index=['two', 'four', 'five'])
frame2['debt'] = val
print(frame2)
为不存在的列赋值会创建出一个新列。关键字 del用于删除列。

作为del的例子,先添加一个新的布尔值的列,state是否为'Ohio'

import pandas as pd
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]}
frame2 = pd.DataFrame(data, columns=['year', 'state', 'pop', 'debt'],
                      index=['one', 'two', 'three', 'four','five', 'six'])
val = pd.Series([-1.2, -1.5, -1.7], index=['two', 'four', 'five'])
frame2['debt'] = val

frame2['eastern'] = frame2.state == 'Ohio'
print(frame2)

del frame2['eastern']
print(frame2)
注意: 通过索引方式返回的列只是相应 数据的视图而已,并不是副本。因此,对返回的 Series所做的任何就地修改全都会反映到源 DataFrame上。通过 Seriescopy方法即可指定复制列。

另一种常见的数据形式是嵌套字典,如果嵌套字典传给DataFramePandas 就会被解释为:外层字典的键作为列,内层键则作为行索引

import pandas as pd
pop1 = {'Nevada': {2001: 2.4, 2002: 2.9},
        'Ohio': {2000: 1.5, 2001: 1.7, 2002: 3.6}}
frame3 = pd.DataFrame(pop1)
print(frame3)
也可以使用类似 NumPy 数组的方法,对 DataFrame进行转置(交换行和列):
import pandas as pd
pop1 = {'Nevada': {2001: 2.4, 2002: 2.9},
        'Ohio': {2000: 1.5, 2001: 1.7, 2002: 3.6}}
frame3 = pd.DataFrame(pop1)
print(frame3.T)
内层字典的键会被合并、排序以形成最终的索引。如果明确指定了索引,则不会这样:
import pandas as pd
pop1 = {'Nevada': {2001: 2.4, 2002: 2.9},
        'Ohio': {2000: 1.5, 2001: 1.7, 2002: 3.6}}
print(pd.DataFrame(pop1, index=[2001, 2002, 2003]))
Series组成的字典差不多也是一样的用法:
import pandas as pd
pop1 = {'Nevada': {2001: 2.4, 2002: 2.9},
        'Ohio': {2000: 1.5, 2001: 1.7, 2002: 3.6}}
frame3 = pd.DataFrame(pop1)
print(type(frame3['Ohio'][:2]))
pdata = {'Ohio': frame3['Ohio'][:-1],
         'Nevada': frame3['Nevada'][:2]}
print(pd.DataFrame(pdata))
下表列出了 DataFrame构造函数所能接受的各种数据:
类型 描述
二维ndarray 数据矩阵,还可以传入行标和列标
由数组、列表或元组组成的字典 每个序列会变成DataFrame的一列。所有序列的长度必须相同
NumPy的结构化/记录数组 类似于“由数组组成的字典”
Series组成的字典 每个Series会成为一列。如果没有显式指定索引,则各Series的索引会被合并成结果的行索引
由字典组成的字典 各内层字典会成为一列。键会被合并成结果的行索引,跟“由Series组成的字典”的情况―样
字典或Series的列表 各项将会成为DataFrame的一行。字典键或Series索引的并集将会成为DataFrame的列标
由列表或元组组成的列表 类似于“二维ndarray"
另一个DataFrame DataFrame的索引将会被沿用,除非显式指定了其他索引
NumPy的MaskedArray 类似于“二维ndarray”的情况,只是掩码值在结果DataFrame会变成NA/缺失值
如果设置了 DataFrameindexcolumnsname属性,则这些信息也会被显示出来:
import pandas as pd
pop1 = {'Nevada': {2001: 2.4, 2002: 2.9},
        'Ohio': {2000: 1.5, 2001: 1.7, 2002: 3.6}}
frame3 = pd.DataFrame(pop1)
frame3.index.name = 'year'
frame3.columns.name = 'state'
print(frame3)
Series一样, values属性也会以二维 ndarray的形式返回 DataFrame中的数据:
import pandas as pd
pop1 = {'Nevada': {2001: 2.4, 2002: 2.9},
        'Ohio': {2000: 1.5, 2001: 1.7, 2002: 3.6}}
frame3 = pd.DataFrame(pop1)
print(frame3.values)
如果 DataFrame各列的数据类型不同,由于 NumPy 数组存储的数据类型需要一致,则值数组的 dtype就会选用 能兼容所有列的数据类型
import pandas as pd
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]}
frame2 = pd.DataFrame(data, columns=['year', 'state', 'pop', 'debt'],
                      index=['one', 'two', 'three', 'four','five', 'six'])
val = pd.Series([-1.2, -1.5, -1.7], index=['two', 'four', 'five'])
frame2['debt'] = val
print(frame2.values)

索引对象

Pandas 的索引对象负责管理轴标签和其他元数据(比如轴名称等)。构建 SeriesDataFrame时,所用到的任何数组或其他序列的标签都会被转换成一个 Index
import pandas as pd
obj = pd.Series(range(3), index=['a', 'b', 'c'])
index = obj.index
print(index)
print(index[1:])
Index对象是不可变的,因此用户不能对其进行修改:
import pandas as pd
obj = pd.Series(range(3), index=['a', 'b', 'c'])
index = obj.index
index[1] = 'd'
不可变可以使 Index对象在多个数据结构之间 安全共享
import pandas as pd

labels = pd.Index(np.arange(3))
print(labels)

obj2 = pd.Series([1.5, -2.5, 0], index=labels)
print(obj2)

print(obj2.index is labels)

obj3 = pd.Series(['a', 'b', 'c'], index=labels)
print(obj3)

print(obj3.index is labels)
除了类似于数组, Index的功能也类似一个固定大小的集合:
import pandas as pd
pop1 = {'Nevada': {2001: 2.4, 2002: 2.9},
        'Ohio': {2000: 1.5, 2001: 1.7, 2002: 3.6}}
frame3 = pd.DataFrame(pop1)
print(frame3)
print(frame3.columns)
print('Ohio' in frame3.columns)
print(2003 in frame3.index)
与 Python 的集合不同, PandasIndex可以包含重复的标签:
import pandas as pd
dup_labels = pd.Index(['foo', 'foo', 'bar', 'bar'])
print(dup_labels)
选择重复的标签,会显示所有的结果。

每个索引都有一些方法和属性,它们可用于设置逻辑并回答有关该索引所包含的数据的常见问题。下表列出了这些方法:

方法 描述
append 连接另一个Index对象,产生一个新的Index
difference 计算差集,并得到一个Index
intersection 计算交集
union 计算并集
isin 计算一个指示各值是否都包含在参数集合中的布尔型数组
delete 删除索引i处的元素,并得到新的Index
drop 删除传入的值,并得到新的Index
insert 将元素插入到索引i处,并得到新的Index
is_monotonic 当各元素均大于等于前一个元素时,返回True
is_unique 当Index没有重复值时,返回True
unique 计算Ilndex中唯一值的数组

2.基本功能

2.1 重新索引

Pandas对象的一个重要方法是 reindex,其作用是创建一个新对象,它的数据符合新的索引。看下面的例子:
import pandas as pd
obj = pd.Series([4.5, 7.2, -5.3, 3.6], index=['d', 'b', 'a', 'c'])
print(obj)
用该 Seriesreindex将会根据新索引进行重排。如果某个索引值当前不存在,就引入缺失值:
import pandas as pd
obj2 = obj.reindex(['a', 'b', 'c', 'd', 'e'])
print(obj2)
对于时间序列这样的有序数据,重新索引时可能需要做一些插值处理。 method选项即可达到此目的,例如,使用 ffill可以实现前向值填充:
import pandas as pd
obj3 = pd.Series(['blue', 'purple', 'yellow'], index=[0, 2, 4])
print(obj3)
print(obj3.reindex(range(6), method='ffill'))
借助 DataFramereindex可以修改(行)索引和列。只传递一个序列时,会重新索引结果的行:
import pandas as pd
frame = pd.DataFrame(np.arange(9).reshape((3,3)), index=['a', 'c', 'd'], columns=['Ohio', 'Texas', 'California'])
print(frame)
frame2 = frame.reindex(['a', 'b', 'c', 'd'])
print(frame2)
列可以用 columns关键字重新索引:
states = ['Texas', 'Utah', 'California']
print(frame.reindex(columns = states))
下表列出了 reindex函数的各参数及描述:
参数 描述
index 用作索引的新序列。既可以是Index实例,也可以是其他序列型的Python数据结构。Index会被完全使用,就像没有任何复制一样
method 插值(填充)方式
fill_value 在重新索引的过程中,需要引入缺失值时使用的替代值
limit 前向或后向填充时的最大填充量
tolerance 向前后向后填充时,填充不准确匹配项的最大间距(绝对值距离)
level Multilndex的指定级别上匹配简单索引,否则选取其子集
copy 默认为True,无论如何都复制;如果为False,则新旧相等就不复制

2.2 丢弃指定轴上的项

丢弃某条轴上的一个或多个项很简单,只要有一个索引数组或列表即可。由于需要执行一些数据整理和集合逻辑,所以 drop方法返回的是一个在指定轴上删除了指定值的新对象:
import pandas as pd
obj = pd.Series(np.arange(5.), index = ['a', 'b', 'c', 'd', 'e'])
print(obj)

new_obj = obj.drop('c')
print(new_obj)

obj.drop(['d', 'c'])
print(obj)
对于 DataFrame,可以删除任意轴上的索引值。为了演示,先新建一个 DataFrame例子:
import pandas as pd
data = pd.DataFrame(np.arange(16).reshape((4, 4)),
                    index=['Ohio', 'Colorado', 'Utah', 'New York'],
                    columns=['one', 'two', 'three', 'four'])
print(data)
用标签序列调用 drop会从行标签(axis 0)删除值:
print(data.drop(['Colorado', 'Ohio']))
通过传递 axis=1axis='columns'可以删除列的值:
print(data.drop('two', axis = 1))
print(data.drop(['two', 'four'], axis='columns'))
print(data)
许多函数带有 inplace参数,如 drop,会修改 SeriesDataFrame的大小或形状,可以就地修改对象,不会返回新的对象:
import pandas as pd
obj = pd.Series(np.arange(5.), index = ['a', 'b', 'c', 'd', 'e'])
print(obj)
obj.drop('c', inplace=True)
print(obj)
小心使用inplace,它会销毁所有被删除的数据。

2.3 索引、选取和过滤

Series索引(obj[...])的工作方式类似于 NumPy 数组的索引,只不过 Series的索引值不只是整数。下面是几个例子:
import pandas as pd
obj = pd.Series(np.arange(4.), index=['a', 'b', 'c', 'd'])
print(obj)
print(obj['b'])
print(obj[1])
print(obj[2:4])
print(obj[['b', 'a', 'd']])
print(obj[[1,3]])
print(obj[obj < 2])
利用标签的切片运算与普通的Python切片运算不同, 其末端是包含的
print(obj['b':'c'])
用切片可以对 Series的相应部分进行设置:
obj['b':'c'] = 5
print(obj)
用一个值或序列对 DataFrame进行索引其实就是获取一个或多个列:
import pandas as pd
data = pd.DataFrame(np.arange(16).reshape((4, 4)),
                    index=['Ohio', 'Colorado', 'Utah', 'New York'],
                    columns=['one', 'two', 'three', 'four'])
print(data)
print(data['two'])
print(data[['three', 'one']])
这种索引方式有几个特殊的情况。首先通过 切片或布尔型数组选取数据:
print(data[:2])
print(data[data['three'] > 5])
选取行的语法 data[:2]十分方便。向[ ]传递单一的元素或列表,就可 选择列

另一种用法是通过布尔型DataFrame(比如下面这个由标量比较运算得出的)进行索引:

print(data < 5)
print(data[data<5])

data[data < 5] = 0
print(data)

2.4 用 loc 和 iloc 进行选取

对于 DataFrame的行的标签索引,引入了特殊的标签运算符 lociloc。它们可以让你用类似 NumPy 的标记,使用轴标签( loc)或整数索引( iloc),从 DataFrame选择行和列的子集。

作为一个初步示例,让我们通过标签选择一行和多列:

import pandas as pd
data = pd.DataFrame(np.arange(16).reshape((4, 4)),
                    index=['Ohio', 'Colorado', 'Utah', 'New York'],
                    columns=['one', 'two', 'three', 'four'])
print(data)
print(data.loc['Colorado', ['two', 'three']])
print(type(data.loc['Colorado', ['two', 'three']]))
print(data.iloc[2,[3,0,1]])
print(data.iloc[2])
print(data.iloc[[1,2], [3,0,1]])
这两个索引函数也适用于一个标签或多个标签的切片:
print(data.loc[:'Utah', 'two'])
print(data.iloc[:, :3][data.three > 5])
Pandas 中,有多个方法可以选取和重新组合数据。下表对 DataFrame进行了总结:
类型 描述
df[val] DataFrame选取单列或一组列;在特殊情况下比较便利:布尔型数组(过滤行)、切片(行切片)、或布尔型DataFrame(根据条件设置值)
df.loc[val] 通过标签,选取DataFrame的单个行或一组行
df.locl:, val] 通过标签,选取单列或列子集
df.loc[val1,val2] 通过标签,同时选取行和列
df.iloc[where] 通过整数位置,从 DataFrame选取单个行或行子集
df.iloc[:,where] 通过整数位置,从 DataFrame选取单个列或列子集
df.iloc[where_i, where_.j] 通过整数位置,同时选取行和列
df.at[label_i, label_j] 通过行和列标签,选取单一的标量
df.iat[i,j] 通过行和列的位置(整数),选取单一的标量
reindex 通过标签选取行或列
get_value, set_value 通过行和列标签选取单一值

2.5 整数索引

处理整数索引的 Pandas 对象常常难住新手,因为它与 Python 内置的列表和元组的索引语法不同。例如,你可能不认为下面的代码会出错:
import pandas as pd
ser = pd.Series(np.arange(3.0))
print(ser)
print(ser[2])
print(ser[-1])
这里, Pandas 可以勉强进行整数索引,但是会导致小bug。我们有包含0,1,2的索引,但是引入用户想要的东西(基于标签或位置的索引)很难,另外,对于非整数索引,不会产生歧义:
import pandas as pd
ser2 = pd.Series(np.arange(3.), index=['a', 'b', 'c'])
print(ser2[-1])
为了进行统一,如果轴索引含有整数,数据选取总会使用标签。为了更准确,请使用 loc(标签)或 iloc(整数):
import pandas as pd
ser = pd.Series(np.arange(3.0))
print(ser)

print(ser[:1])
print(ser.loc[:1])
print(ser.iloc[:1])
注意区分以上三个语句的结果。

2.6 算术运算和数据对齐

Pandas 最重要的一个功能是,它可以对不同索引的对象进行算术运算。在将对象相加时,如果存在不同的索引对,则结果的索引就是该索引对的并集。对于有数据库经验的用户,这就像在索引标签上进行自动外连接。看一个简单的例子:
import pandas as pd
s1 = pd.Series([7.3, -2.5, 3.4, 1.5], index=['a', 'c', 'd', 'e'])
s2 = pd.Series([-2.1, 3.6, -1.5, 4, 3.1], index=['a', 'c', 'e', 'f', 'g'])
print(s1)
print(s2)
将它们相加,自动的数据对齐操作在不重叠的索引处引入了NA值。缺失值会在算术运算过程中传播。
print(s1 + s2)
对于 DataFrame,对齐操作会同时发生在行和列上:
import pandas as pd
df1 = pd.DataFrame(np.arange(9.0).reshape((3,3)), columns=list('bcd'),
                   index = ['Ohio', 'Texas', 'Colorado'])
df2 = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'),
                   index=['Utah', 'Ohio', 'Texas', 'Oregon'])
print(df1)
print(df2)
把它们相加后将会返回一个新的 DataFrame,其索引和列为原来那两个 DataFrame的并集:
print(df1 + df2)
如果 DataFrame对象相加,没有共用的列或行标签,结果都会是空:
import pandas as pd
df1 = pd.DataFrame({'A':[1,2]})
df2 = pd.DataFrame({'B':[3,4]})
print(df1)
print(df2)
print(df1 - df2)

2.7 在算术方法中填充值

在对不同索引的对象进行算术运算时,你可能希望当一个对象中某个轴标签在另一个对象中找不到时填充一个特殊值(比如0):
import pandas as pd
df1 = pd.DataFrame(np.arange(12.).reshape((3, 4)),
                   columns=list('abcd'))
df2 = pd.DataFrame(np.arange(20.).reshape((4, 5)),
                   columns=list('abcde'))
df2.loc[1, 'b'] = np.nan
print(df1)
print(df2)
将它们相加时,没有重叠的位置就会产生NA值:
print(df1 + df2)
使用 df1add方法,传入 df2以及一个 fill_value参数:
print(df1.add(df2, fill_value=0))
下表列出了 SeriesDataFrame的算术方法:
方法 描述
add, radd 用于加法(+)的方法
sub, rsub 用于减法(-)的方法
div, rdiv 用于除法(/)的方法
floordiv, rfloordiv 用于整除(//)的方法
mul, rmul 用于乘法(*)的方法
pow, rpow 用于指数(**)的方法
它们每个都有一个副本,以字母r开头,它会翻转参数。因此这两个语句是等价的:
print(1 / df1)
print(df1.rdiv(1))
与此类似,在对 SeriesDataFrame重新索引时,也可以指定一个填充值:
print(df1.reindex(columns=df2.columns, fill_value=0))

2.8 DataFrame 和 Series 之间的运算

跟不同维度的 NumPy 数组一样, DataFrameSeries之间算术运算也是有明确规定的。先来看一个具有启发性的例子,计算一个二维数组与其某行之间的差:
import pandas as pd
arr = np.arange(12.).reshape((3,4))
print(arr)
print(arr[0])
print(arr[0].shape)
print(arr - arr[0])
当我们从 arr减去 arr[0],每一行都会执行这个操作。这就叫做 广播(broadcasting)。 DataFrameSeries之间的运算差不多也是如此:
import pandas as pd
frame = pd.DataFrame(np.arange(12.).reshape((4, 3)),
                     columns=list('bde'),
                     index=['Utah', 'Ohio', 'Texas', 'Oregon'])
series = frame.iloc[0]
print(frame)
print(series)
默认情况下, DataFrameSeries之间的算术运算会将 Series的索引匹配到 DataFrame的列,然后沿着行一直向下广播:
print(frame - series)
如果某个索引值在 DataFrame的列或 Series的索引中找不到,则参与运算的两个对象就会被重新索引以形成并集:
series2 = pd.Series(range(3), index=['b','e','f'])
print(series2)
print(frame + series2)
如果你希望匹配行且在列上广播,则必须使用 算术运算方法。例如:
series3 = frame['d']
print(frame)
print(series3)
print(frame.sub(series3, axis='index'))
传入的轴号就是希望匹配的轴。在本例中,我们的目的是匹配 DataFrame的行索引( axis='index' or axis=0)并进行广播。

2.9 函数应用和映射

NumPy 的 ufuncs(元素级数组方法)也可用于操作 Pandas 对象:
import pandas as pd
frame = pd.DataFrame(np.random.randn(4, 3), columns=list('bde'),
                     index=['Utah', 'Ohio', 'Texas', 'Oregon'])
print(frame)
print(np.abs(frame))
另一个常见的操作是,将函数应用到由各列或行所形成的一维数组上。 DataFrameapply方法即可实现此功能:
f = lambda x: x.max() - x.min()
print(frame.apply(f))
这里的函数 f,计算了一个 Series的最大值和最小值的差,在 frame的每列都执行了一次。结果是一个 Series,使用 frame的列作为索引。

如果传递axis='columns'apply,这个函数会在每行执行:

print(frame.apply(f, axis='columns'))
许多最为常见的数组统计功能都被实现成 DataFrame的方法(如 summean),因此无需使用 apply方法。

传递到apply的函数不是必须返回一个标量,还可以返回由多个值组成的Series

def f(x):
    return pd.Series([x.min(), x.max()], index=['min', 'max'])
print(frame.apply(f))
元素级的 Python 函数也是可以用的。假如你想得到 frame中各个浮点值的格式化字符串,使用 applymap即可:
formater = lambda x: '%.2f' % x
print(frame.applymap(formater))
之所以叫做 applymap,是因为 Series有一个用于应用元素级函数的 map方法:
print(frame['e'].map(formater))

2.10 排序和排名

根据条件对数据集排序(sorting)也是一种重要的内置运算。要对行或列索引进行排序(按字典顺序),可使用 sort_index方法,它将返回一个已排序的新对象:
import pandas as pd
obj = pd.Series(range(4), index=['d', 'a', 'b','c'])
print(obj.sort_index())
对于 DataFrame,则可以根据任意一个轴上的索引进行排序:
import pandas as pd
frame = pd.DataFrame(np.arange(8).reshape((2, 4)),
                     index=['three', 'one'],
                     columns=['d', 'a', 'b', 'c'])
print(frame.sort_index())
print(frame.sort_index(axis=1))
数据默认是按升序排序的,但也可以降序排序:
print(frame.sort_index(axis=1, ascending=False))
若要按值对 Series进行排序,可使用其 sort_values方法:
import pandas as pd
obj = pd.Series([4, 7, -3, 2])
print(obj.sort_values())
在排序时,任何缺失值默认都会被放到 Series的末尾:
import pandas as pd
obj = pd.Series([4, np.nan, 7, np.nan, -3, 2])
print(obj.sort_values())
当排序一个 DataFrame时,你可能希望根据一个或多个列中的值进行排序。将一个或多个列的名字传递给 sort_valuesby选项即可达到该目的:
import pandas as pd
frame = pd.DataFrame({'b': [4, 7, -3, 2], 'a':[0, 1, 0, 1]})
print(frame)
print(frame.sort_values(by='b'))
要根据多个列进行排序,传入名称的列表即可:
print(frame.sort_values(by=['a', 'b']))
接下来介绍 SeriesDataFramerank方法。默认情况下, rank是通过“为各组分配一个平均排名”的方式破坏平级关系的:
import pandas as pd
obj = pd.Series([7, -5, 7, 4, 2, 0, 4])
print(obj.rank())
也可以根据值在原数据中出现的顺序给出排名:
print(obj.rank(method='first'))
这里,条目 02没有使用平均排名 6.5,它们被设成了 67,因为数据中标签 0位于标签 2的前面。

你也可以按降序进行排名:

print(obj.rank(ascending=False, method='max'))
DataFrame可以在行或列上计算排名:
import pandas as pd
frame = pd.DataFrame({'b': [4.3, 7, -3, 2], 'a': [0, 1, 0, 1],
                      'c': [-2, 5, 8, -2.5]})
print(frame)
print(frame.rank(axis='columns'))
print(frame.rank())
下表列出了所有用于破坏平级关系的 method选项:
方法 描述
'average' 默认:在相等分组中,为各个值分配平均排名
'min' 使用整个分组的最小排名
'max' 使用整个分组的最大排名
'first' 按值在原始数据中的出现顺序分配排名
'dense' 类似于'min'方法,但是排名总是在组间增加1,而不是组中相同的元素数

2.11 带有重复标签的轴索引

直到目前为止,所介绍的所有范例都有着唯一的轴标签(索引值)。虽然许多 Pandas 函数(如 reindex)都要求标签唯一,但这并不是强制性的。我们来看看下面这个简单的带有重复索引值的 Series
import pandas as pd
obj = pd.Series(range(5), index=['a', 'a', 'b', 'b', 'c'])
print(obj)
索引的 is_unique属性可以告诉你它的值是否是唯一的:
print(obj.index.is_unique)
对于带有重复值的索引,数据选取的行为将会有些不同。如果某个索引对应多个值,则返回一个 Series;而对应单个值的,则返回一个标量值:
print(obj['a'])
print(obj['c'])
这样会使代码变复杂,因为索引的输出类型会根据标签是否有重复发生变化。

DataFrame的行进行索引时也是如此:

import pandas as pd
df = pd.DataFrame(np.random.randn(4, 3), index=['a', 'a', 'b', 'b'])
print(df)
print(df.loc['b'])

3.汇总和计算描述统计

Pandas 对象拥有一组常用的数学和统计方法。它们大部分都属于约简和汇总统计,用于从 Series中提取单个值(如 summean)或从 DataFrame的行或列中提取一个 Series。跟对应的 NumPy 数组方法相比,它们都是基于没有缺失数据的假设而构建的。看一个简单的 DataFrame
import pandas as pd
df = pd.DataFrame([[1.4, np.nan], [7.1, -4.5],
                   [np.nan, np.nan], [0.75, -1.3]],
                  index = ['a', 'b', 'c', 'd'],
                  columns=['one','two'])
print(df)
调用 DataFramesum方法将会返回一个含有列的和的 Series
print(df.sum())
传入 axis='columns'axis=1将会按行进行求和运算:
print(df.sum(axis=1))
NA值会自动被排除,除非整个切片(这里指的是行或列)都是NA。通过 skipna选项可以禁用该功能:
print(df.mean(axis='columns', skipna=False))
下表列出了这些约简方法的常用选项:
选项 描述
axis 约简的轴。DataFrame的行用0,列用1
skipna 排除缺失值,默认值为True
level 如果轴是层次化索引的(即Multilndex),则根据level分组约简
有些方法(如 idxminidxmax)返回的是间接统计(比如达到最小值或最大值的索引):
import pandas as pd
df = pd.DataFrame([[1.4, np.nan], [7.1, -4.5],
                   [np.nan, np.nan], [0.75, -1.3]],
                  index = ['a', 'b', 'c', 'd'],
                  columns=['one','two'])
print(df.idxmax())
另一些方法则是累计型的:
print(df.cumsum())
还有一种方法,它既不是约简型也不是累计型。 describe就是一个例子,它用于一次性产生多个汇总统计:
print(df.describe())
对于非数值型数据, describe会产生另外一种汇总统计:
import pandas as pd
obj = pd.Series(['a', 'a', 'b', 'c'] * 4)
print(obj)
print(obj.describe())
下表列出了所有与描述统计相关的方法:
方法 描述
count 非NA值的数量
describe 针对Series或各DataFrame列计算汇总统计
minmax 计算最小值和最大值
argminargmax 计算能够获取到最小值和最大值的索引位置(整数)
idxminidxmax 计算能够获取到最小值和最大值的索引值
quantile 计算样本的分位数(0到1)
sum 值的总和
mean 值的平均数
median 值的算术中位数(50%分位数)
mad 根据平均值计算平均绝对离差
var 样本值的方差
std 样本值的标准差
skew 样本值的偏度(三阶矩)
kurt 样本值的峰度(四阶矩)
cumsum 样本值的累计和
cummincummax 样本值的累计最大值和累计最小值
cumprod 样本值的累计积
diff 计算一阶差分(对时间序列很有用)
pct_change 计算百分数变化

3.1 相关系数与协方差

有些汇总统计(如相关系数和协方差)是通过参数对计算出来的。我们来看几个 DataFrame,它们的数据来自Yahoo!Finance的股票价格和成交量,使用的是 pandas-datareader包(可以用 condapip安装):
import pandas as pd
import pandas_datareader.data as web
all_data = {ticker: web.get_data_yahoo(ticker)
            for ticker in ['AAPL', 'IBM', 'MSFT', 'GOOG']}

price = pd.DataFrame({ticker: data['Adj Close']
                     for ticker, data in all_data.items()})
volume = pd.DataFrame({ticker: data['Volume']
                      for ticker, data in all_data.items()})

all_data.items()
for ticker, data in all_data.items():
    print(ticker)
    print(type(data['Volume']))

print(price.head())
print(volume.head())
现在计算价格的百分数变化:
returns = price.pct_change()
print(returns.head())
Seriescorr方法用于计算两个 Series中重叠的、非NA的、按索引对齐的值的相关系数。与此类似, cov用于计算协方差:
print(returns['MSFT'].corr(returns['IBM']))
print(returns['MSFT'].cov(returns['IBM']))
因为MSTF是一个合理的 Python 属性,我们还可以用更简洁的语法选择列:
print(returns.MSFT.corr(returns.IBM))
另一方面, DataFramecorrcov方法将以 DataFrame的形式分别返回完整的相关系数或协方差矩阵:
print(returns.corr())
print(returns.cov())
利用 DataFramecorrwith方法,你可以计算其列或行跟另一个 SeriesDataFrame之间的相关系数。传入一个 Series将会返回一个相关系数值 Series(针对各列进行计算):
print(returns.corrwith(returns.IBM))
传入一个 DataFrame则会计算按列名配对的相关系数。这里,计算百分比变化与成交量的相关系数:
print(returns.corrwith(volume))
传入 axis='columns'即可按行进行计算。无论如何,在计算相关系数之前,所有的数据项都会按标签对齐。

3.2 唯一值、值计数以及成员资格

还有一类方法可以从一维 Series的值中抽取信息。看下面的例子:
import pandas as pd
obj = pd.Series(['c', 'a', 'd', 'a', 'a', 'b', 'b', 'c', 'c'])
第一个函数是 unique,它可以得到 Series中的唯一值数组:
uniques = obj.unique()
print(uniques)
返回的唯一值是未排序的,如果需要的话,可以对结果再次进行排序( uniques.sort())。相似的, value_counts用于计算一个 Series中各值出现的频率:
print(pd.value_counts(obj.values, sort=False))
isin用于判断矢量化集合的成员资格,可用于过滤 Series中或 DataFrame列中数据的子集:
print(obj)
mask = obj.isin(['b', 'c'])
print(mask)
print(obj[mask])
isin类似的是 Index.get_indexer方法,它可以给你一个索引数组,从可能包含重复值的数组到另一个不同值的数组:
to_match = pd.Series(['c', 'a', 'b', 'b', 'c', 'a'])
unique_vals = pd.Series(['c', 'b' ,'a'])
print(pd.Index(unique_vals).get_indexer(to_match))
下表给出了这几个方法的一些参考信息:
方法 描述
isin 计算一个表示“Series各值是否包含于传入的值序列中”的布尔型数组
match 计算一个数组中的各值到另一个不同值数组的整数索引;对于数据对齐和连接类型的操作十分有用
unique 计算Series中的唯一值数组,按发现的顺序返回
value_counts 返回一个Series,其索引为唯一值,其值为频率,按计数值降序排列
有时,你可能希望得到 DataFrame中多个相关列的一张柱状图。例如:
import pandas as pd
data = pd.DataFrame({'Qu1': [1, 3, 4, 3, 4],
                     'Qu2': [2, 3, 1, 2, 3],
                     'Qu3': [1, 5, 2, 4, 4]})
print(data)
pandas.value_counts传给该 DataFrameapply函数,就会出现:
result = data.apply(pd.value_counts).fillna(0)
print(result)
这里,结果中的行标签是 所有列的唯一值。后面的频率值是每个列中这些值的相应计数。
目录
相关文章
|
12天前
|
数据采集 数据挖掘 数据处理
使用Python和Pandas进行数据分析基础
使用Python和Pandas进行数据分析基础
34 5
|
1天前
|
数据可视化 数据挖掘 API
使用Python进行数据可视化:探索Matplotlib和Seaborn库
【9月更文挑战第19天】在数据科学领域,将复杂的数据集转换成直观、易懂的图形是一项基本而关键的技能。本文旨在通过Python编程语言介绍两个强大的数据可视化库——Matplotlib和Seaborn,以及它们如何帮助数据分析师和研究人员揭示数据背后的故事。我们将从基础概念讲起,逐步深入到高级技巧,确保无论读者的背景如何,都能获得必要的知识和启发,以在自己的项目中实现有效的数据可视化。
|
4天前
|
并行计算 算法 Java
优化Python数据处理性能的策略
在数据密集型应用中,Python常因其解释性语言的特性而面临性能瓶颈。本文探讨了提升Python数据处理性能的几种策略,包括优化数据结构的选择、使用高效的库以及应用并行处理技术。通过具体示例和代码演示,读者将了解如何在实际开发中应用这些策略,从而显著提升数据处理速度。
|
1天前
|
机器学习/深度学习 JSON API
HTTP协议实战演练场:Python requests库助你成为网络数据抓取大师
在数据驱动的时代,网络数据抓取对于数据分析、机器学习等至关重要。HTTP协议作为互联网通信的基石,其重要性不言而喻。Python的`requests`库凭借简洁的API和强大的功能,成为网络数据抓取的利器。本文将通过实战演练展示如何使用`requests`库进行数据抓取,包括发送GET/POST请求、处理JSON响应及添加自定义请求头等。首先,请确保已安装`requests`库,可通过`pip install requests`进行安装。接下来,我们将逐一介绍如何利用`requests`库探索网络世界,助你成为数据抓取大师。在实践过程中,务必遵守相关法律法规和网站使用条款,做到技术与道德并重。
7 2
|
4天前
|
SQL 前端开发 数据可视化
Rodeo支持多种Python库
Rodeo支持多种Python库
16 5
|
3天前
|
数据采集 存储 JSON
从零到一构建网络爬虫帝国:HTTP协议+Python requests库深度解析
在网络数据的海洋中,网络爬虫遵循HTTP协议,穿梭于互联网各处,收集宝贵信息。本文将从零开始,使用Python的requests库,深入解析HTTP协议,助你构建自己的网络爬虫帝国。首先介绍HTTP协议基础,包括请求与响应结构;然后详细介绍requests库的安装与使用,演示如何发送GET和POST请求并处理响应;最后概述爬虫构建流程及挑战,帮助你逐步掌握核心技术,畅游数据海洋。
18 3
|
4天前
|
存储 并行计算 大数据
优化Python数据处理性能的最佳实践
在数据科学和大数据时代,优化Python数据处理性能变得至关重要。通过探讨数据处理瓶颈、内存管理、并行计算以及高效库的使用,本篇文章旨在提供切实可行的最佳实践,以帮助开发者提升数据处理效率。
|
8天前
|
数据采集 网络协议 API
HTTP协议大揭秘!Python requests库实战,让网络请求变得简单高效
【9月更文挑战第13天】在数字化时代,互联网成为信息传输的核心平台,HTTP协议作为基石,定义了客户端与服务器间的数据传输规则。直接处理HTTP请求复杂繁琐,但Python的`requests`库提供了一个简洁强大的接口,简化了这一过程。HTTP协议采用请求与响应模式,无状态且结构化设计,使其能灵活处理各种数据交换。
37 8
|
12天前
|
JSON API 开发者
Python网络编程新纪元:urllib与requests库,让你的HTTP请求无所不能
【9月更文挑战第9天】随着互联网的发展,网络编程成为现代软件开发的关键部分。Python凭借简洁、易读及强大的特性,在该领域展现出独特魅力。本文介绍了Python标准库中的`urllib`和第三方库`requests`在处理HTTP请求方面的优势。`urllib`虽API底层但功能全面,适用于深入控制HTTP请求;而`requests`则以简洁的API和人性化设计著称,使HTTP请求变得简单高效。两者互补共存,共同推动Python网络编程进入全新纪元,无论初学者还是资深开发者都能从中受益。
31 7
|
11天前
|
数据采集 数据挖掘 数据处理
使用Python和Pandas处理CSV数据
使用Python和Pandas处理CSV数据
43 5