数据分析和建模方面的大量编程工作都是用在数据准备上的:加载、清理、转换以及重塑。有时候,存放在文件或数据库中的数据并不能满足你的数据处理应用的要求。
合并数据集
- 数据库风格的DataFrame合并
数据集的合并(merge)或连接(join)运算是通过一个或多个键将行链接起来的。这些运算是关系型数据库的核心。pandas的merge函数是对数据应用这些算法的主要切入点。
from pandas import Series,DataFrame
import pandas as pd
df1 = DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'],'data1': range(7)})
df2 = DataFrame({'key': ['a', 'b', 'd'],'data2': range(3)})
print(pd.merge(df1,df2))
print(pd.merge(df1,df2,on='key'))
#output
key data1 data2
0 b 0 1
1 b 1 1
2 b 6 1
3 a 2 0
4 a 4 0
5 a 5 0
key data1 data2
0 b 0 1
1 b 1 1
2 b 6 1
3 a 2 0
4 a 4 0
5 a 5 0
这是一种多对一的合并。df1中的数据有多个被标记为a和b的行,而df2中key列的每个值则仅对应一行。
如果两个对象的列名不同,也可以分别进行指定:
df1 = DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'],'data1': range(7)})
df2 = DataFrame({'key': ['a', 'b', 'd'],'data2': range(3)})
print(pd.merge(df1,df2,left_on='data1',right_on='data2'))
#output
key_x data1 key_y data2
0 b 0 a 0
1 b 1 b 1
2 a 2 d 2
默认情况下,merge做的是"inner"连接;结果中的键是交集。其他方式还有"left"、“right"以及"outer”。外连接求取的是键的并集,组合了左连接和右连接的效果:(也就是集合的内连接、左连接、右连接、外连接)
print(pd.merge(df1,df2,left_on='data1',right_on='data2',how='left'))
#output
key_x data1 key_y data2
0 b 0 a 0.0
1 b 1 b 1.0
2 a 2 d 2.0
3 c 3 NaN NaN
4 a 4 NaN NaN
5 a 5 NaN NaN
6 b 6 NaN NaN
多对多连接产生的是行的笛卡尔积。原理相似,不做介绍。
- 索引上的合并
pd.merge(left1, right1, left_on='key', right_index=True)
- 轴向连接
暂略
- 合并重叠数据
还有一种数据组合问题不能用简单的合并(merge)或连接(concatenation)运算来处理。比如说,你可能有索引全部或部分重叠的两个数据集。给这个例子增加一点启发性,我们使用NumPy的where函数,它用于表达一种矢量化的if-else:
暂略
重塑和轴向旋转
有许多用于重新排列表格型数据的基础运算。这些函数也称作重塑(reshape)或轴向旋转(pivot)运算。
- 重塑层次化索引
层次化索引为DataFrame数据的重排任务提供了一种具有良好一致性的方式。主要功能有二:
- stack:将数据的列“旋转”为行。
- unstack:将数据的行“旋转”为列。
前面已经展示过操作。这里略去。
一般来说就是把DataFrame stack为层次化索引的Series,把层次化索引的Series unstack为 DataFrame。
- 将“长格式”旋转为“宽格式”
时间序列数据通常是以所谓的“长格式”(long)或“堆叠格式”(stacked)存储在数据库和CSV中的:
In [116]: ldata[:10]
Out[116]:
date item value
0 1959-03-31 00:00:00 realgdp 2710.349
1 1959-03-31 00:00:00 infl 0.000
2 1959-03-31 00:00:00 unemp 5.800
3 1959-06-30 00:00:00 realgdp 2778.801
4 1959-06-30 00:00:00 infl 2.340
5 1959-06-30 00:00:00 unemp 5.100
6 1959-09-30 00:00:00 realgdp 2775.488
7 1959-09-30 00:00:00 infl 2.740
8 1959-09-30 00:00:00 unemp 5.300
9 1959-12-31 00:00:00 realgdp 2785.204
关系型数据库(如MySQL)中的数据经常都是这样存储的,因为固定架构(即列名和数据类型)有一个好处:随着表中数据的添加或删除,item列中的值的种类能够增加或减少。在上面那个例子中,date和item通常就是主键(用关系型数据库的说法),不仅提供了关系完整性,而且提供了更为简单的查询支持。
当然这也是有缺点的:长格式的数据操作起来可能不那么轻松。你可能会更喜欢DataFrame,不同的item值分别形成一列,date列中的时间值则用作索引。
DataFrame的pivot方法完全可以实现这个转换:
In [117]: pivoted = ldata.pivot('date', 'item', 'value')
In [118]: pivoted.head()
Out[118]:
item infl realgdp unemp
date
1959-03-31 0.00 2710.349 5.8
1959-06-30 2.34 2778.801 5.1
1959-09-30 2.74 2775.488 5.3
1959-12-31 0.27 2785.204 5.6
1960-03-31 2.31 2847.699 5.2
前两个参数值分别用作行和列索引的列名,最后一个参数值则是用于填充DataFrame的数据列的列名。
数据转换
- 移除重复数据
DataFrame中常常会出现重复行。下面就是一个例子:
from pandas import Series,DataFrame
import pandas as pd
data = DataFrame({'k1': ['one'] * 3 + ['two'] * 4,
'k2': [1, 1, 2, 3, 3, 4, 4]})
print(data)
DataFrame的duplicated方法返回一个布尔型Series,表示各行是否是重复行。还有一个与此相关的drop_duplicates方法,它用于返回一个移除了重复行的Data-Frame。
print(data.duplicated())
print(data.drop_duplicates())
#output
0 False
1 True
2 False
3 False
4 True
5 False
6 True
dtype: bool
k1 k2
0 one 1
2 one 2
3 two 3
5 two 4
这两个方法默认会判断全部列,你也可以指定部分列进行重复项判断。假设你还有一列值,且只希望根据k1列过滤重复项:
In [130]: data['v1'] = range(7)
In [131]: data.drop_duplicates(['k1'])
Out[131]:
k1 k2 v1
0 one 1 0
3 two 3 3
duplicated和drop_duplicates默认保留的是第一个出现的值组合。传入take_last=True则保留最后一个:
In [132]: data.drop_duplicates(['k1', 'k2'], take_last=True)
Out[132]:
k1 k2 v1
1 one 1 1
2 one 2 2
4 two 3 4
6 two 4 6
- 利用函数或映射进行数据转换
在对数据集进行转换时,你可能希望根据数组、Series或DataFrame列中的值来实现该转换工作。我们来看看下面这组有关肉类的数据:
In [133]: data = DataFrame({'food': ['bacon', 'pulled pork', 'bacon', 'Pastrami',
...: 'corned beef', 'Bacon', 'pastrami', 'honey ham',
...: 'nova lox'],
...: 'ounces': [4, 3, 12, 6, 7.5, 8, 3, 5, 6]})
In [134]: data
Out[134]:
food ounces
0 bacon 4.0
1 pulled pork 3.0
2 bacon 12.0
3 Pastrami 6.0
4 corned beef 7.5
5 Bacon 8.0
6 pastrami 3.0
7 honey ham 5.0
8 nova lox 6.0
假设你想要添加一列表示该肉类食物来源的动物类型。我们先编写一个肉类到动物的映射:
meat_to_animal = {
'bacon': 'pig',
'pulled pork': 'pig',
'pastrami': 'cow',
'corned beef': 'cow',
'honey ham': 'pig',
'nova lox': 'salmon'
}
Series的map方法可以接受一个函数或含有映射关系的字典型对象,但是这里有一个小问题,即有些肉类的首字母大写了,而另一些则没有。因此,我们还需要将各个值转换为小写:
In [
136]: data['animal'] =
data['food'].map(str.lower).map(meat_to_animal)
In [137]: data
Out[137]:
food ounces animal
0 bacon 4.0 pig
1 pulled pork 3.0 pig
2 bacon 12.0 pig
3 Pastrami 6.0 cow
4 corned beef 7.5 cow
5 Bacon 8.0 pig
6 pastrami 3.0 cow
7 honey ham 5.0 pig
8 nova lox 6.0 salmon
- 替换值
利用fillna方法填充缺失数据可以看做值替换的一种特殊情况。虽然前面提到的map可用于修改对象的数据子集,而replace则提供了一种实现该功能的更简单、更灵活的方式。
#3种方式
In [142]: data.replace([-999, -1000], np.nan)
In [143]: data.replace([-999, -1000], [np.nan, 0])
In [144]: data.replace({-999: np.nan, -1000: 0})
- 重命名轴索引
暂略
- 离散化和面元划分
暂略
- 检测和过滤异常值
来看一个含有正态分布数据的DataFrame:
In [170]: np.random.seed(12345)
In [171]: data = DataFrame(np.random.randn(1000, 4))
In [172]: data.describe()
Out[172]:
0 1 2 3
count 1000.000000 1000.000000 1000.000000 1000.000000
mean -0.067684 0.067924 0.025598 -0.002298
std 0.998035 0.992106 1.006835 0.996794
min -3.428254 -3.548824 -3.184377 -3.745356
25% -0.774890 -0.591841 -0.641675 -0.644144
50% -0.116401 0.101143 0.002073 -0.013611
75% 0.616366 0.780282 0.680391 0.654328
max 3.366626 2.653656 3.260383 3.927528
假设你想要找出某列中绝对值大小超过3的值:
In [173]: col = data[3]
In [174]: col[np.abs(col) > 3]
Out[174]:
97 3.927528
305 -3.399312
400 -3.745356
Name: 3
要选出全部含有“超过3或-3的值”的行,你可以利用布尔型DataFrame以及any方法:
In [175]: data[(np.abs(data) > 3).any(1)]
- 排列和随机采样
利用numpy.random.permutation函数可以轻松实现对Series或DataFrame的列的排列工作。
暂略
- 计算指标/哑变量
暂略
字符串操作
以后更新