具有功能记忆状态的 Groupby
Groupby with function remembering state
我正在处理多行,我想根据当前行中的一个值 x 是否在前一行中的值 x 的 100 以内来对它们进行分组。
举个例子
5, "hello"
10, "was"
60, "bla"
5000, "qwerty"
"hello"、"was"和"bla"应该是一组,"qwerty"是另一组。
有没有办法巧妙地用groupby解决这个问题?我能想到的所有解决方案都感觉有点老套,比如使用其中包含先前值的字典默认参数,并在每次调用 groupby 中的函数(键)时更新它。
itertools.groupby
可能有一些巧妙的技巧,但它很简单,可以为您的特定问题编写自定义生成器函数。也许是这样的(未经测试):
def grouper(it):
group = []
for item in it:
if not group or abs(int(item[0]) - int(group[-1][0])) < 100:
group.append(item)
else:
yield group
group = [item]
if group: # yield final group if not empty
yield group
用法类似于
with open(filename) as fid:
for group in grouper(line.split(',') for line in fid):
# do something with group
for item in group:
# do something with item
你可以写一个简单的class来封装临时变量,然后使用class的方法作为关键函数:
class KeyClass(object):
def __init__(self):
self.lastValue = None
self.currentKey = 1
def handleVal(self, val):
if self.lastValue is not None and abs(val - self.lastValue) > 100:
self.currentKey += 1
self.lastValue = val
return self.currentKey
>>> [(k, list(g)) for k, g in itertools.groupby(data, KeyClass().handleVal)]
[(1, [1, 2, 100, 105]), (2, [300, 350, 375]), (3, [500]), (4, [800, 808])]
只是为了好玩,我还想出了这种相当费脑筋的方法,使用高级生成器的 send
方法作为关键函数:
def keyGen():
curKey = 1
newVal = yield None
while True:
oldVal, newVal = newVal, (yield curKey)
if oldVal is None or abs(newVal-oldVal) > 100:
curKey += 1
key = keyGen()
next(key)
>>> [(k, list(g)) for k, g in itertools.groupby(data, key.send)]
[(1, [1, 2, 100, 105]), (2, [300, 350, 375]), (3, [500]), (4, [800, 808])]
思考一下这可能是一个很好的理解练习.send
(这是给我的!)。
我正在处理多行,我想根据当前行中的一个值 x 是否在前一行中的值 x 的 100 以内来对它们进行分组。
举个例子
5, "hello"
10, "was"
60, "bla"
5000, "qwerty"
"hello"、"was"和"bla"应该是一组,"qwerty"是另一组。
有没有办法巧妙地用groupby解决这个问题?我能想到的所有解决方案都感觉有点老套,比如使用其中包含先前值的字典默认参数,并在每次调用 groupby 中的函数(键)时更新它。
itertools.groupby
可能有一些巧妙的技巧,但它很简单,可以为您的特定问题编写自定义生成器函数。也许是这样的(未经测试):
def grouper(it):
group = []
for item in it:
if not group or abs(int(item[0]) - int(group[-1][0])) < 100:
group.append(item)
else:
yield group
group = [item]
if group: # yield final group if not empty
yield group
用法类似于
with open(filename) as fid:
for group in grouper(line.split(',') for line in fid):
# do something with group
for item in group:
# do something with item
你可以写一个简单的class来封装临时变量,然后使用class的方法作为关键函数:
class KeyClass(object):
def __init__(self):
self.lastValue = None
self.currentKey = 1
def handleVal(self, val):
if self.lastValue is not None and abs(val - self.lastValue) > 100:
self.currentKey += 1
self.lastValue = val
return self.currentKey
>>> [(k, list(g)) for k, g in itertools.groupby(data, KeyClass().handleVal)]
[(1, [1, 2, 100, 105]), (2, [300, 350, 375]), (3, [500]), (4, [800, 808])]
只是为了好玩,我还想出了这种相当费脑筋的方法,使用高级生成器的 send
方法作为关键函数:
def keyGen():
curKey = 1
newVal = yield None
while True:
oldVal, newVal = newVal, (yield curKey)
if oldVal is None or abs(newVal-oldVal) > 100:
curKey += 1
key = keyGen()
next(key)
>>> [(k, list(g)) for k, g in itertools.groupby(data, key.send)]
[(1, [1, 2, 100, 105]), (2, [300, 350, 375]), (3, [500]), (4, [800, 808])]
思考一下这可能是一个很好的理解练习.send
(这是给我的!)。