python数据分析使用的第三方类库(用tidyverse的方式玩转)
python数据分析使用的第三方类库(用tidyverse的方式玩转)In [7]: df >> define(z=if_else('x > 1' 1 0)) Out[7]: x y z 0 0 zero 0 1 1 one 0 2 2 two 1 3 3 three 1 与 R 中使用 tidyverse 包中的函数类似,也可以将数据框作为函数的第一个参数In [5]: df Out[5]: x y 0 0 zero 1 1 one 2 2 two 3 3 three In [6]: df >> define(z='x') Out[6]: x y z 0 0 zero 0 1 1 one 1 2 2 two 2 3 3 three 3 使用 if_
前言今天,我们要介绍一个用于数据操作的 Python 第三方库 —— plydata,这个库是基于 R 中的 dplyr、tidyr 和 forcats 包,许多函数名称都是直接借用过来的
plydata 使用 >> 作为管道操作符,用于替代 ply(data *verbs) 函数,目前只支持 pandas 的 DataFrame 数据结构,后续可能还会添加对数据库的支持
下面我们来看看怎么使用 plydata 来玩转数据操作吧
使用首先,使用 pip 进行安装
pip install plydata
我们先举个简单的例子,先导入对应的模块
import numpy as np
import pandas as pd
from plydata import define query if_else ply
创建一个 DataFrame
df = pd.DataFrame({
'x': [0 1 2 3]
'y': ['zero' 'one' 'two' 'three']}
)
使用 define 函数为数据框添加一列(或者使用 mutate 函数,两者相同,对应于 tidyverse 中的同名函数)
In [5]: df
Out[5]:
x y
0 0 zero
1 1 one
2 2 two
3 3 three
In [6]: df >> define(z='x')
Out[6]:
x y z
0 0 zero 0
1 1 one 1
2 2 two 2
3 3 three 3
使用 if_else 来添加不同的值
In [7]: df >> define(z=if_else('x > 1' 1 0))
Out[7]:
x y z
0 0 zero 0
1 1 one 0
2 2 two 1
3 3 three 1
与 R 中使用 tidyverse 包中的函数类似,也可以将数据框作为函数的第一个参数
In [8]: query(df 'x > 1')
Out[8]:
x y
2 2 two
3 3 three
# 等同于下面的操作
In [9]: df >> query('x > 1')
Out[9]:
x y
2 2 two
3 3 three
或者使用 ply 函数代替管道操作,例如
In [10]: ply(df
...: define(z=if_else('x > 1' 1 0))
...: query('z == 1')
...: )
Out[10]:
x y z
2 2 two 1
3 3 three 1
将每一次的操作作为参数,传递给 ply 函数
如果与 plotnine(基于 ggplot2 的 Python 实现)联用,可以很容易将 R 的绘图代码,转换为 Python。
例如,对于如下 R 代码,绘制 sin(x) 函数在 [0 2] 区间的图形
library(tidyverse)
tibble(x = seq(0 2*pi length.out = 500)) %>%
mutate(y = sin(x) sign = if_else(y >= 0 "positive" "negative")) %>%
ggplot(aes(x y))
geom_line(aes(colour = sign) size = 1.5)
转换为 Python 代码
from plotnine import ggplot aes geom_line
(
pd.DataFrame({'x': np.linspace(0 2*np.pi 500)})
>> define(y='np.sin(x)')
>> define(sign=if_else('y >= 0' '"positive"' '"negative"'))
>> (ggplot(aes('x' 'y'))
geom_line(aes(color='sign') size=1.5))
)
注意:在 Python 中,运算表达式都放置在引号内部,且字符串要表示为嵌套的引号
使用 call 函数为数据框执行外部函数或 pd.DataFrame 函数,例如,应用外部函数
In [11]: df = pd.DataFrame({
...: 'A': {0: 'a' 1: 'b' 2: 'c'}
...: 'B': {0: 1 1: 3 2: 5}
...: 'C': {0: 2 1: 4 2: np.nan}
...: })
In [12]: df >> call(pd.melt)
Out[12]:
variable value
0 A a
1 A b
2 A c
3 B 1
4 B 3
5 B 5
6 C 2.0
7 C 4.0
8 C NaN
In [13]: df >> call(pd.melt id_vars=['A'] value_vars=['B'])
Out[13]:
A variable value
0 a B 1
1 b B 3
2 c B 5
应用对象方法
In [14]: df >> call('.dropna' axis=1)
Out[14]:
A B
0 a 1
1 b 3
2 c 5
In [15]: (df
...: >> call(pd.melt)
...: >> query('variable != "B"')
...: >> call('.reset_index' drop=True)
...: )
Out[15]:
variable value
0 A a
1 A b
2 A c
3 C 2.0
4 C 4.0
5 C NaN
示例1. 单表操作
单表操作主要包括如下函数
其功能都与 dplyr 包中的同名函数一样
例如,mutate 函数添加列
In [16]: df >> mutate(x_sq = 'x**2')
Out[16]:
x x_sq
0 1 1
1 2 4
2 3 9
In [17]: df >> mutate(('x*2' 'x*2') ('x*3' 'x*3') x_cubed='x**3')
Out[17]:
x x*2 x*3 x_cubed
0 1 2 3 1
1 2 4 6 8
2 3 6 9 27
使用 arrange 函数对数据框进行排序
In [18]: df = pd.DataFrame({'x': [1 5 2 2 4 0]
...: 'y': [1 2 3 4 5 6]})
In [19]: df >> arrange('x')
Out[19]:
x y
5 0 6
0 1 1
2 2 3
3 2 4
4 4 5
1 5 2
In [20]: df >> arrange('x' '-y')
Out[20]:
x y
5 0 6
0 1 1
3 2 4
2 2 3
4 4 5
1 5 2
In [21]: df >> arrange('np.sin(y)')
Out[21]:
x y
4 4 5
3 2 4
5 0 6
2 2 3
0 1 1
1 5 2
使用 group_by 进行分组
In [22]: df = pd.DataFrame({'x': [1 5 2 2 4 0 4]
...: 'y': [1 2 3 4 5 6 5]})
In [23]: df >> group_by('x')
Out[23]:
groups: ['x']
x y
0 1 1
1 5 2
2 2 3
3 2 4
4 4 5
5 0 6
6 4 5
In [24]: df >> group_by('x') >> group_indices()
Out[24]: array([1 4 2 2 3 0 3])
类似于 define,group_by 可以添加新列
In [25]: df >> group_by('y-1' xplus1='x 1')
Out[25]:
groups: ['y-1' 'xplus1']
x y y-1 xplus1
0 1 1 0 2
1 5 2 1 6
2 2 3 2 3
3 2 4 3 3
4 4 5 4 5
5 0 6 5 1
6 4 5 4 5
如果后续的动词未使用分组信息,则新产生的列将会保留在数据框中
In [26]: df >> group_by('y-1' xplus1='x 1') >> select('y')
Out[26]:
groups: ['y-1' 'xplus1']
y-1 xplus1 y
0 0 2 1
1 1 6 2
2 2 3 3
3 3 3 4
4 4 5 5
5 5 1 6
6 4 5 5
使用 query 来进行行过滤
In [27]: df = pd.DataFrame({'x': [0 1 2 3 4 5]
...: 'y': [0 0 1 1 2 3]})
In [28]: df >> query('x % 2 == 0')
Out[28]:
x y
0 0 0
2 2 1
4 4 2
In [29]: df >> query('x % 2 == 0 and y > 0')
Out[29]:
x y
2 2 1
4 4 2
In [30]: df >> query('x % 2 == 0 & y > 0')
Out[30]:
x y
2 2 1
4 4 2
In [31]: df >> group_by('y') >> query('x == x.min()')
Out[31]:
groups: ['y']
x y
0 0 0
2 2 1
4 4 2
5 5 3
使用 summarize 函数对数据进行统计
In [32]: df = pd.DataFrame({'x': [1 5 2 2 4 0 4]
...: 'y': [1 2 3 4 5 6 5]
...: 'z': [1 3 3 4 5 5 5]})
In [33]: df >> summarize('np.sum(x)' max='np.max(x)')
Out[33]:
np.sum(x) max
0 18 5
In [34]: df >> group_by('y' 'z') >> summarize(mean_x='np.mean(x)')
Out[34]:
y z mean_x
0 1 1 1.0
1 2 3 5.0
2 3 3 2.0
3 4 4 2.0
4 5 5 4.0
5 6 5 0.0
支持如下函数:
- min(x) - numpy.amin() 的别名
- max(x) - numpy.amax() 的别名
- sum(x) - numpy.sum() 的别名
- cumsum(x) - numpy.cumsum() 的别名
- mean(x) - numpy.mean() 的别名
- median(x) - numpy.median() 的别名
- std(x) - numpy.std() 的别名
- first(x) - x 的第一个元素
- last(x) - x 的最后一个元素
- nth(x n) - x 的第 n 个值或 numpy.nan
- n_distinct(x) - x 中唯一值的个数
- n_unique(x) - n_distinct 的别名
- n() - 当前分组的个数
In [35]: df >> summarize('min(x)' 'max(x)' 'mean(x)' 'sum(x)'
...: 'first(x)' 'last(x)' 'nth(x 3)')
Out[35]:
min(x) max(x) mean(x) sum(x) first(x) last(x) nth(x 3)
0 0 5 2.571429 18 1 4 2
分组求值
In [36]: df >> group_by('y') >> summarize(y_count='n()')
Out[36]:
y y_count
0 1 1
1 2 1
2 3 1
3 4 1
4 5 2
5 6 1
In [37]: df >> group_by('y') >> summarize('mean(x)')
Out[37]:
y mean(x)
0 1 1.0
1 2 5.0
2 3 2.0
3 4 2.0
4 5 4.0
5 6 0.0
2. 双表操作
双表操作主要包含:
与 dplyr 同名函数执行相同的功能。例如
In [38]: df1 = pd.DataFrame({
...: 'col1': ['one' 'two' 'three']
...: 'col2': [1 2 3]
...: })
In [39]: df2 = pd.DataFrame({
...: 'col1': ['one' 'four' 'three']
...: 'col2': [1 4 3]
...: })
In [40]: anti_join(df1 df2 on='col1')
Out[40]:
col1 col2
1 two 2
In [41]: outer_join(df1 df2 on='col1')
Out[41]:
col1 col2_x col2_y
0 one 1.0 1.0
1 two 2.0 NaN
2 three 3.0 3.0
3 four NaN 4.0
In [42]: inner_join(df1 df2 on='col1')
Out[42]:
col1 col2_x col2_y
0 one 1 1
1 three 3 3
In [43]: left_join(df1 df2 on='col1')
Out[43]:
col1 col2_x col2_y
0 one 1 1.0
1 two 2 NaN
2 three 3 3.0
In [44]: right_join(df1 df2 on='col1')
Out[44]:
col1 col2_x col2_y
0 one 1.0 1
1 four NaN 4
2 three 3.0 3
In [45]: semi_join(df1 df2 on='col1')
Out[45]:
col1 col2
0 one 1
2 three 3
3. Tidy Verbs3.1 数据透视表
- gather
In [48]: from plydata.tidy import *
In [49]: df = pd.DataFrame({
...: 'name': ['mary' 'oscar' 'martha' 'john']
...: 'math': [92 83 85 90]
...: 'art': [75 95 80 72]
...: })
In [50]: df >> gather('subject' 'grade' ['math' 'art'])
Out[50]:
name subject grade
0 mary math 92
1 oscar math 83
2 martha math 85
3 john math 90
4 mary art 75
5 oscar art 95
6 martha art 80
7 john art 72
- pivot_longer
In [51]: df = pd.DataFrame({
...: 'name': ['mary' 'mary' 'john' 'john']
...: 'city':['dakar' 'dakar' 'lome' 'lome']
...: 'year': [1990 1992 1996 1998]
...: 'data_t1_sunny': [8 6 4 7]
...: 'data_t2_rainy': [9 7 7 6]
...: })
In [52]: df >> pivot_longer(
...: cols=select(startswith='data')
...: names_to=['take' 'season']
...: values_to='score'
...: names_pattern=r'data_(t\d)(_\w )'
...: names_prefix={'take': 't' 'season': '_'}
...: )
Out[52]:
name city year take season score
0 mary dakar 1990 1 sunny 8
1 mary dakar 1992 1 sunny 6
2 john lome 1996 1 sunny 4
3 john lome 1998 1 sunny 7
4 mary dakar 1990 2 rainy 9
5 mary dakar 1992 2 rainy 7
6 john lome 1996 2 rainy 7
7 john lome 1998 2 rainy 6
- pivot_wider
In [53]: df = pd.DataFrame({
...: 'name': ['mary' 'oscar' 'martha' 'john'] * 2
...: 'initials': ['M.K' 'O.S' 'M.J' 'J.T'] * 2
...: 'subject': np.repeat(['math' 'art'] 4)
...: 'grade': [92 83 85 90 75 95 80 72]
...: 'midterm': [88 83 89 93 85 95 76 79]
...: })
In [54]: df >> pivot_wider(
...: names_from='subject'
...: values_from=('grade' 'midterm')
...: )
Out[54]:
initials name grade_art grade_math midterm_art midterm_math
0 J.T john 72 90 79 93
1 M.J martha 80 85 76 89
2 M.K mary 75 92 85 88
3 O.S oscar 95 83 95 83
- spread
In [55]: df = pd.DataFrame({
...: 'name': ['mary' 'oscar' 'martha' 'john'] * 2
...: 'subject': np.repeat(['math' 'art'] 4)
...: 'grade': [92 83 85 90 75 95 80 72]
...: })
In [56]: df >> spread('subject' 'grade')
Out[56]:
name art math
0 john 72 90
1 martha 80 85
2 mary 75 92
3 oscar 95 83
3.2 字符串列
- extract:使用正则表达式分割字符串列
- separate:使用指定分隔符分割字符串列
- separate_rows:将一行变量的值分割为多行
In [57]: df = pd.DataFrame({
...: 'parent': ['martha' 'james' 'alice']
...: 'child': ['leah' 'joe vinny laura' 'pat lee']
...: 'age': ['3' '12 6 4' '2 7']
...: })
In [58]: df >> separate_rows('child' 'age')
Out[58]:
parent child age
0 martha leah 3
1 james joe 12
2 james vinny 6
3 james laura 4
4 alice pat 2
5 alice lee 7
- unite:将多列合并为一列
plydata 还提供了很多函数用于处理分类变量,我们就不再一一说明了,感兴趣的可以通过查阅下面的文档来学习,每个函数都有对应的例子,清晰易懂
https://plydata.readthedocs.io/en/stable/api.html