数据规整化
重塑和轴向旋转
对表格型数据重新排列。
重塑层次化索引
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')
方法 | 说明 |
---|---|
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() |
有两个办法可以实现矢量化的元素获取操作:要么使用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() |
总结
数据规整化到这章结束,下一步就是数据可视化。
个人微信公众号:BrainZou
网友评论