Python:按投影值相等将列表分组为子列表
Python: group a list into sublists by a equality of projected value
是否有一种很好的 pythonic 方法可以将列表分组为列表的列表,其中每个内部列表仅包含那些具有相同投影的元素,由用户定义为函数?
示例:
>>> x = [0, 1, 2, 3, 4, 5, 6, 7]
>>> groupby(x, projection=lambda e: e % 3)
[[0, 3, 6], [1, 4, 7], [2, 5]]
我不关心投影本身,只是如果它对于某些元素是相等的,那么这些元素必须最终出现在同一个子列表中。
我基本上是在寻找 python 等同于 haskell 函数 GHC.Exts.groupWith
:
Prelude> import GHC.Exts
Prelude GHC.Exts> groupWith (`mod` 3) [0..7]
[[0,3,6],[1,4,7],[2,5]]
标准库中的 itertools
模块包含一个 groupby()
函数,可以执行您想要的操作。
请注意,groupby()
的输入应按组键排序,以使每个组只产生一次,但使用相同的键函数进行排序很容易。因此,如果您的关键函数(投影)正在查看数字是否为偶数,则它看起来像这样:
from itertools import groupby
x = [0, 1, 2, 3, 4, 5, 6, 7]
def projection(val):
return val % 3
x_sorted = sorted(x, key=projection)
x_grouped = [list(it) for k, it in groupby(x_sorted, projection)]
print(x_grouped)
[[0, 3, 6], [1, 4, 7], [2, 5]]
请注意,虽然此版本仅使用标准 Python 功能,但如果您处理的值可能超过 100.000 个,则应查看 pandas(请参阅@ayhan 的回答)
无需排序。
from collections import defaultdict
def groupby(iterable, projection):
result = defaultdict(list)
for item in iterable:
result[projection(item)].append(item)
return result
x = [0, 1, 2, 3, 4, 5, 6, 7]
groups = groupby(x, projection=lambda e: e % 3)
print groups
print groups[0]
输出:
defaultdict(<type 'list'>, {0: [0, 3, 6], 1: [1, 4, 7], 2: [2, 5]})
[0, 3, 6]
这是一种使用 itertools
中的 compress
的方法:
from itertools import compress
import numpy as np
L = [i %3 for i in x]
[list(compress(x, np.array(L)==i)) for i in set(L)]
#[[0, 3, 6], [1, 4, 7], [2, 5]]
一个pandas版本应该是这样的:
import pandas as pd
x = [0, 1, 2, 3, 4, 5, 6, 7]
pd.Series(x).groupby(lambda t: t%3).groups
Out[13]: {0: [0, 3, 6], 1: [1, 4, 7], 2: [2, 5]}
或者
pd.Series(x).groupby(lambda t: t%3).groups.values()
Out[32]: dict_values([[0, 3, 6], [1, 4, 7], [2, 5]])
是否有一种很好的 pythonic 方法可以将列表分组为列表的列表,其中每个内部列表仅包含那些具有相同投影的元素,由用户定义为函数?
示例:
>>> x = [0, 1, 2, 3, 4, 5, 6, 7]
>>> groupby(x, projection=lambda e: e % 3)
[[0, 3, 6], [1, 4, 7], [2, 5]]
我不关心投影本身,只是如果它对于某些元素是相等的,那么这些元素必须最终出现在同一个子列表中。
我基本上是在寻找 python 等同于 haskell 函数 GHC.Exts.groupWith
:
Prelude> import GHC.Exts
Prelude GHC.Exts> groupWith (`mod` 3) [0..7]
[[0,3,6],[1,4,7],[2,5]]
标准库中的 itertools
模块包含一个 groupby()
函数,可以执行您想要的操作。
请注意,groupby()
的输入应按组键排序,以使每个组只产生一次,但使用相同的键函数进行排序很容易。因此,如果您的关键函数(投影)正在查看数字是否为偶数,则它看起来像这样:
from itertools import groupby
x = [0, 1, 2, 3, 4, 5, 6, 7]
def projection(val):
return val % 3
x_sorted = sorted(x, key=projection)
x_grouped = [list(it) for k, it in groupby(x_sorted, projection)]
print(x_grouped)
[[0, 3, 6], [1, 4, 7], [2, 5]]
请注意,虽然此版本仅使用标准 Python 功能,但如果您处理的值可能超过 100.000 个,则应查看 pandas(请参阅@ayhan 的回答)
无需排序。
from collections import defaultdict
def groupby(iterable, projection):
result = defaultdict(list)
for item in iterable:
result[projection(item)].append(item)
return result
x = [0, 1, 2, 3, 4, 5, 6, 7]
groups = groupby(x, projection=lambda e: e % 3)
print groups
print groups[0]
输出:
defaultdict(<type 'list'>, {0: [0, 3, 6], 1: [1, 4, 7], 2: [2, 5]})
[0, 3, 6]
这是一种使用 itertools
中的 compress
的方法:
from itertools import compress
import numpy as np
L = [i %3 for i in x]
[list(compress(x, np.array(L)==i)) for i in set(L)]
#[[0, 3, 6], [1, 4, 7], [2, 5]]
一个pandas版本应该是这样的:
import pandas as pd
x = [0, 1, 2, 3, 4, 5, 6, 7]
pd.Series(x).groupby(lambda t: t%3).groups
Out[13]: {0: [0, 3, 6], 1: [1, 4, 7], 2: [2, 5]}
或者
pd.Series(x).groupby(lambda t: t%3).groups.values()
Out[32]: dict_values([[0, 3, 6], [1, 4, 7], [2, 5]])