利用Python进行数据分析——数据规整化

数据分析和建模方面的大量编程工作都是用在数据准备上的:加载、清理、转换以及重塑。有时候,存放在文件或数据库中的数据并不能满足你的数据处理应用的要求。

合并数据集


  • 数据库风格的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的列的排列工作。
暂略


  • 计算指标/哑变量

暂略

字符串操作

以后更新

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值