Pipe

mapfilter是处理iterable数据最好用的函数,但却让代码看起来很乱,使代码可读性大大降低。

arr = [1, 2, 3, 4, 5]

#对arr筛选偶数,并对偶数乘以2
list(map(lambda x: x*2, filter(lambda x:x%2==0, arr)))
[4, 8]

刚刚的iterable的例子,其实可以使用pipe库中的 | 来应用多种方法。

from pipe import select, where

arr = [1, 2, 3, 4, 5]

list(arr
    |where(lambda x:x%2==0)
    |select(lambda x:x*2))
[4, 8]

pipe是什么?

pipe是python中的管道操作库,可以使数据分析多个步骤(函数)像管道(流水线)一样上下衔接,共同完成一个数据分析任务。

我喜欢pipe是因为它让iterable代码变得干净整洁,可读性大大增强。后面我会通过几个案例让大家快速掌握pipe库。首先先安装pipe

!pip3 install pipe

点击下载本文代码

where

对iterable中的数据进行筛选操作

from pipe import where

arr = [1, 2, 3, 4, 5]

#把偶数筛选出来
list(arr | where(lambda x: x%2==0))
[2, 4]

select

对iterable中的数据进行某种操作

from pipe import select

arr = [1, 2, 3, 4, 5]

#对arr中的每个数 乘以2
list(arr | select(lambda x: x*2))
[2, 4, 6, 8, 10]

现在你可能会有疑问: 为何在Python已拥有map和filter情况下, 还用pipe库中的 select和 where呢?

因为可以使用管道在一个方法后面加入另一个方法, 加不止1次!!

from pipe import select, where

arr = [1, 2, 3, 4, 5]

list(arr 
     | where(lambda x: x%2==0) #筛选arr中的偶数
     | select(lambda x: x*2)  #对偶数乘以2
    )
[4, 8]

非折叠iterable

chain

对于嵌套结构的iterable数据,最难任务之一就是将其展平。

from pipe import chain

nested = [[1,2,[3]], [4, 5]]

list((nested | chain))
[1, 2, [3], 4, 5]

即时经过上述操作, 依然不是完全展开。 为了处理深度嵌套数据, 可以使用traverse方法。

traverse

遍历traverse方法可以用递归的方式展开 嵌套对象。

from pipe import traverse

nested = [[1,2,[3]], [4, 5]]

list((nested | traverse))
[1, 2, 3, 4, 5]

现在我们从抽取字典values中的列表,并将其展平

from pipe import traverse, select

fruits = [
    {"name": "apple", "price": [2, 5]},
    {"name": "orange", "price": 4},
    {"name": "grape", "price": 5}
]

list(fruits
    | select(lambda fruit: fruit["price"])
    | traverse)
[2, 5, 4, 5]

groupby

有时候,需要对列表中的数据进行分组,这可能用到groupby方法。

from pipe import select, groupby

list(
    (1, 2, 3, 4, 5, 6, 7, 8, 9)
    | groupby(lambda x: "偶数" if x%2==0 else "奇数")
    | select(lambda x: {x[0]: list(x[1])})
)
[{'偶数': [2, 4, 6, 8]}, {'奇数': [1, 3, 5, 7, 9]}]

在上面的代码中, 我们使用groupby将数字分为奇数组和偶数组。groupby方法输出的结果如下

[('偶数', <itertools._grouper at 0x10bd54550>),
 ('奇数', <itertools._grouper at 0x10bd4d350>)]
 

接下来,使用select将元素为元组的列表转化为字典,其中

  • 元组中第1位置做字典的关键词
  • 元组中第2位置做字典的值
[{'偶数': [2, 4, 6, 8]}, {'奇数': [1, 3, 5, 7, 9]}]

Cool!为了range值大于2, 我们在select内增加where条件操作

from pipe import select, groupby

list(
    (1, 2, 3, 4, 5, 6, 7, 8, 9)
    | groupby(lambda x: "偶数" if x%2==0 else "奇数")
    | select(lambda x: {x[0]: list(x[1] 
                                   | where(lambda x: x>2)
                                  )
                       }
            )
)
[{'偶数': [4, 6, 8]}, {'奇数': [3, 5, 7, 9]}]

dedup

使用Key对list数据进行去重

from pipe import dedup

arr = [1, 2, 2, 3, 4, 5, 6, 6, 7, 9, 3, 3, 1]

list(arr | dedup)
[1, 2, 3, 4, 5, 6, 7, 9]

这看起来没啥新意,毕竟python内置的set函数即可实现刚刚的需求。然而,dedup通过key获得列表中的唯一元素。

例如,获得小于5的唯一元素, 且另一个元素大于或等于5

from pipe import dedup

arr = [1, 2, 2, 3, 4, 5, 6, 6, 7, 9, 3, 3, 1]

list(arr | dedup(lambda key: key<5))
[1, 5]
from pipe import traverse, select

data = [
    {"name": "apple", "count": 2},
    {"name": "orange", "count": 4},
    {"name": "grape", "count": None},
    {"name": "orange", "count": 7}
]

list(
    data
    | dedup(key=lambda fruit: fruit["name"])
    | select(lambda fruit: fruit["count"])
    | where(lambda count: isinstance(count, int))
)
[2, 4]

了解课程

点击上方图片购买课程

点击进入详情页