你好,游客 登录
背景:
阅读新闻

利用Python进行数据分析(十一)之数据规整化

[日期:2018-03-07] 来源:稀土掘金  作者: [字体: ]

数据规整化

重塑和轴向旋转

对表格型数据重新排列。

重塑层次化索引

stack:将数据的列“旋转”为行。

unstack:将数据的行“旋转”为列。

data:
number      one      two    three
state          
Ohio          0        1       2
Colorado      3        4       5

data.stack()结果如下:为一个Series

state        number
Ohio            one          0
                two          1
                three        2
Colorado
                one          3
                two          4
                three        5

反过来就是用unstack()

将“长格式”旋转为“宽格式”

长格式为全部列像关系型数据库(如MySQL)存储,固定架构,后续增加数据或者删除,数据只会越来越长。但是缺点是数据操作不方便。因为都是一行做一个数据,看上去数据就特别多,但是如果将一个作为索引,就能减少一定量的数据级,但是数据量并没减少。

pivoted = ldata.pivot('date', 'item', 'value')
#用DataFrame的pivot的方法实现date作行索引,item做列,value为值。
#但是pivot只是一个快捷方式,并没有改变源数据的格式,所以如果需要改变源格式,需要结合上面重塑层次化索引来操作:
unstacked =  ldata.set_index(['date','item']).unstack('item')

数据转换

前面提到的是对数据进行重排,另一类重要操作则是过滤、清理以及其他的

转换工作。

移除重复操作

DataFrame中出现了重复行时:

data:
   k1   k2
o  one   1
I  one   1
2  one   2
3  two   3
4  two   3
5  two   4
6  two   4
# 返回每行是否为重复行(与之前的列比较,所以第一次出现时是false,第二次及之后就是true了)
data.duplicated() :
0    False
1    True
2    False
3    False
4    True
5    False
6    True
#返回移除了重复行的DataFrame(默认判断所有列)
data.drop_duplicates()
#返回移除了重复行的DataFrame(只判断某列)
data.drop_duplicates(['k1'])
#drop_duplicates默认保留的是第一次出现的组合,传入take_last=True则保留最后一个:
data.drop_duplicates(['k1','k2'],take_last=True)

利用函数或映射进行数据转换

假设一个data如下:

data:
          food  ounces
0        bacon     4.0
1  pulled pork     3.0
2        bacon    12.0
3     Pastrami     6.0
4  corned beef     7.5
S        Bacon     8.0
6     pastrami     3.0
7    honey ham     5.0
8     nova lox     6.0

如果想添加一列表明该food来源的动物类型,需要先编写一个肉类到动物的映射:

meat_to_animal = {
'bacon': 'pig',
'pulled pork': 'pig',
'pastrami': 'cow',
'corned beef': 'cow',
'honey ham': 'pig',
'nova lox': 'salmon'
}

如何增加上去,注意到有的肉类有大写,str.lower为一个函数,可以放在Series的map方法中,再把映射也可以放进来:

data['animal'] = data['food'].map(str.lower).map(meat_to_animal)

而使用lambda则更简洁:

data['animal'] = data['food'].map(lambda x:meat_to_animal[x..lower()])

替换值

之前处理缺失时,使用fillna方法填充可以看作是一种特殊情况,前面的map当然也可以用来替换,但是replace是一种更方便灵活的方法。

#把data的-999替换为缺失值
data.replace(-999,np.nan)
#一次替换多个值
data.replace([-999,-1000],np.nan)
#对多个不同值不同的替换,使用替换关系组成的列表即可
data.replace([-999,-1000],[np.nan,0])
#或者传入字典
data.replace({-999:np.nan,-1000:0})

重命名轴索引

对轴索引重新命名,也可以使用map来传入一个函数等操作

#如下 对index索引名字大写
data.index = data.index.map(str.upper)

如果想创建数据集的转换版而不是修改原始数据,则可以使用rename:

data.rename(index = str.title,columns =str.upper)
#index首字母大写,columns全部大写
#传入字典即可对部分轴标签替换'OHIO'替换为 'INDIANA''three'替换'peekaboo'
data.rename(index={'OHIO': 'INDIANA'},
            columns={'three': 'peekaboo'})

rename不是修改源数据,如果想直接修改,在rename内直接传入inplace=True即可。

离散化和面元划分

我的理解就是对一组数据划分到不同的组内:

假设有一组人员数据,而你希望将它们划分为不同的年龄组。

In [153]: ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32]

接下来将这些数据划分为“18到25"、"26到35"、"35到60’以及“60以上’几个面元。要实现该功能.需要使用panda的cut函数:

In [154]: bins = [18, 25, 35, 60, 100]
In [155]: cats = pd.cut(ages, bins)
In [156]: cats
Out[156]:
Lategorlcal:
array([(18, 25], (18, 25], (18, 25), (25, 35], (18, 25], (18, 25],
(35, 60], (25, 35], (60, 100], (35, 60], (35, 60], (25, 35]], dtype=object)
Levels (4): Index([(18, 25], (25, 35], (35, 60], (60, 100]], dtype=object)

实际上是先定义一个labels指明每个值分别位于哪个区间(labels),然后对区间进行替换显示(levels)

cats.labels
array([0, 0, 0, 1, 0, 0, 2, 1, 3, 2, 2, 1])

默认区间为左开右闭,可以通过 pd.cut(ages, [18, 26, 36, 61, 100], right=False) 来修改为左闭右开。这个labels负责最后的显示,所以

group names = ['Youth', 'YoungAdult', 'MiddleAged', 'Senior']
pd.cut(ages, bins, labels=group_names)
#最后就不再是输出(18, 25]这种,而是输出Youth,YoungAdult等

如果传入面元数量(分组数量),则会根据数据最小值和最大值计算等长面元。

#将一些均匀分布数据分为四组
data = np.randam.rand(20)
pd.cut(data, 4, precision=2)

qcut是类似cut的函数,但是qcut是使用的样本分位数,所以得到大小基本相等的面元。即每个面元,每个分组的数据数量基本相同。 pd.qcut(data,4) 即将数据均匀撒在四个分组中。

#当然也不一定每个分组的数据数量要相同
pd.qcut(data, [0, 0.1, 0.5, 0.9, 1."];
#就是按照1:4:4:1比例来分组。

检测和过滤异常值

#找出某列中绝对值大于3的值:
col=data[3]
col[np.abs(col)>3]
#选出所有含有超过3或-3的值的行,可以利用布尔型DataFrame以及any方法:
data[(np.abs(data) > 3).any(1)]
#将值限制在区间-3到3以内:(大于3的取3 小于-3的取-3)
data[np.abs(data) > 3] = np.sign(data) *3
#sign为取符号正数为1负数为-1 0为0

排列和随机采样

#reshape(5,4)分成5行4列
df = DataFrame(np.arange(5 * 4).reshape(5, 4))
#permutation(5)排列
sampler = np.random.permutation(5)
sampler:
              array((1, 0, 2, 3, 4])
df.take(sampler)
#即会把df的第1行和第0行互换排列
permutation返回的数组中切下前3个元素
df.take(np.random.permutation(len(df))[:3])
#从一个数组里,随机取10个里面的值:
bag = np.array((S, 7, -1, 6, 4])
sampler = np.random.randint(0, len(bag), size=10)
sampler:
          array([4, 4, 2, 2, 2, 0, 3, 0, 4, 1])
draws = bag.take(sampler)
draws:
          array([ 4,  4, -1, -1, -1,  5,  6,  5,  4,  7])

计算指标/哑变量

DataFrame的某一列中含有k个不同的值,则可以派生出一个k列矩阵或DataFrame(其值全为1和0) .pandas有一个get _dummies函数可以实现该功能。

df = DataFrame({'key':['b', 'b', 'a', 'c', 'a','b'],'data1': range(6)})
pd.get_dummies(df['key'])
   a  b  c
0  0  1  0
1  0  1  0
2  1  0  0
3  0  0  1
4  1  0  0
5  0  1  0

DataFrame列加上一个前缀,abc即变为key_a,key_b,key_c

dummies = pd.get_dummies(df['key']), prefix='key')

字符串操作

Python能够成为流行的数据处理语言,部分原因是其简单易用的字符申和文本处理功能。大部分文本运算都直接做成了字符串对象的内置方法。对于更为复杂的模式匹配和

文本操作。则可能需要用到正则表达式。pandas对此进行了加强。它使你能够对整组数据应用字符串表达式和正则表达式.而且能处理烦人的缺失数据。

字符串对象方法

split,strip等这些内置的字符串方法已经满足来了大部分要求。

pieces=['a', 'b', 'guido'] 

利用加法,可以将这些子字符串以双冒号分隔符的形式连接起来:

first, second, third = pieces
first + '::'+ second +'::'+ third
      'a::b::guido'

但是python则可以直接: '::'.join(pieces) 
等等,其他内置方法不再赘述。使用时百度查表即可。

正则表达式

正则表达式觉得是一种很高级简洁的方法,且其他地方用的也很多,如果不了解,建议搜索查看一遍,有所印象。掌握常用的一些用法。

pandas中矢量化的字符串函数

通过data.map,所有字符串和正则表达式方法都能被应用于(传人lambda表达式或其他函数)各个值,但是如果存在NA就会报错。为了解决这个问题.Series有一些能够跳过NA值的字符串操作方法。通过Series的str属性即可访问这些方法。例如,我们可以通过str.contains检查各个电子邮件地址是否含有“gmail":

data.str.contains('gmail')

有两个办法可以实现矢量化的元素获取操作:要么使用str.get,要么在str属性上使用

索引。

matches = data.str.match(pattern, flags=re.IGNORECASE); 

矢量化的字符串方法如下:

方法说明
cat 实现元素级的字符串连接操作,可指定分隔符
contains 返回表示各字符串是否含有指定模式的布尔型数组
count 模式的出现次数
endswith, startswith 相当于对各个元素执行x.endswith(pattern)或x.startswith (pattern)
findall 计算各字符串的模式列表
get 获取各元素的第i个字符
join 根据指定的分隔符将Series中各元紊的字符串连接起来
len 计算各字符串的长度
lower, upper 转换大小写。相当于对各个元素执行x.lower()或x.upper()
match 根据指定的正则表达式对各个元素执行re.match
pad 在字符串的左边、右边或左右两边添加空白符
center 相当于pad(side='both')
repeat 重复值。例如,s.str.repeat(3)相当于对各个字符串执行x*3
replace 用指定字符串替换找到的模式
slice 对Series中的各个字符串进行子串截取
split 根据分隔符或正则表达式对字符串进行拆分
strip. rstrip, lstrip 去除空白符,包括换行符。相当于对各个元素执行x.strip(), x.rstripp, x.lstrip()

总结

数据规整化到这章结束,下一步就是数据可视化





收藏 推荐 打印 | 录入:Cstor | 阅读:
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数
点评:
       
评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款