用 tidyverse 的方式玩转 Python 数据处理

用 tidyverse 的方式玩转 Python 数据处理

前言

今天,我们要介绍一个用于数据操作的 Python 第三方库 —— plydata,这个库是基于 R 中的 dplyrtidyrforcats 包,许多函数名称都是直接借用过来的

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(基于 ggplot2Python 实现)联用,可以很容易将 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])

类似于 definegroup_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 Verbs

3.1 数据透视表
  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
  1. 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
  1. 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
  1. 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 字符串列
  1. extract:使用正则表达式分割字符串列
  2. separate:使用指定分隔符分割字符串列
  3. 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
  1. unite:将多列合并为一列

4. 总结

plydata 还提供了很多函数用于处理分类变量,我们就不再一一说明了,感兴趣的可以通过查阅下面的文档来学习,每个函数都有对应的例子,清晰易懂

https://plydata.readthedocs.io/en/stable/api.html

  • 10
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

名本无名

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值