[英]Max and Min for several fields inside PCollection in apache beam with python
我正在通过python SDK使用apache Beam,并遇到以下问题:
我有一个大约有1百万个条目的PCollection,一个PCollection中的每个条目看起来像一个长度为150的2元组[(key1,value1),(key2,value2),...]
。 我需要在每个键的PCollection的所有条目中找到最大值和最小值,以便对值进行规范化。
理想情况下,获得带有元组列表[(key,max_value,min_value),...]
然后可以很容易地进行规范化以获得[(key1,norm_value1),(key2,norm_value2),...]
,其中norm_value = (value - min) / (max - min)
目前,我只能手动对每个键分别进行操作,这不是很方便也不可持续,因此任何建议都会有所帮助。
我决定使用自定义的CombineFn
函数确定每个键的最小值和最大值。 然后,使用CoGroupByKey
将它们与输入数据连接起来,并应用所需的映射以标准化值。
"""Normalize PCollection values."""
import logging
import argparse
import sys
import apache_beam as beam
from apache_beam.io import WriteToText
from apache_beam.options.pipeline_options import PipelineOptions
# custom CombineFn that outputs min and max value
class MinMaxFn(beam.CombineFn):
# initialize min and max values (I assumed int type)
def create_accumulator(self):
return (sys.maxint, 0)
# update if current value is a new min or max
def add_input(self, min_max, input):
(current_min, current_max) = min_max
return min(current_min, input), max(current_max, input)
def merge_accumulators(self, accumulators):
return accumulators
def extract_output(self, min_max):
return min_max
def run(argv=None):
"""Main entry point; defines and runs the pipeline."""
parser = argparse.ArgumentParser()
parser.add_argument('--output',
dest='output',
required=True,
help='Output file to write results to.')
known_args, pipeline_args = parser.parse_known_args(argv)
pipeline_options = PipelineOptions(pipeline_args)
p = beam.Pipeline(options=pipeline_options)
# create test data
pc = [('foo', 1), ('bar', 5), ('foo', 5), ('bar', 9), ('bar', 2)]
# first run through data to apply custom combineFn and determine min/max per key
minmax = pc | 'Determine Min Max' >> beam.CombinePerKey(MinMaxFn())
# group input data by key and append corresponding min and max
merged = (pc, minmax) | 'Join Pcollections' >> beam.CoGroupByKey()
# apply mapping to normalize values according to 'norm_value = (value - min) / (max - min)'
normalized = merged | 'Normalize values' >> beam.Map(lambda (a, (b, c)): (a, [float(val - c[0][0][0])/(c[0][0][1] -c[0][0][0]) for val in b]))
# write results to output file
normalized | 'Write results' >> WriteToText(known_args.output)
result = p.run()
result.wait_until_finish()
if __name__ == '__main__':
logging.getLogger().setLevel(logging.INFO)
run()
可以使用python SCRIPT_NAME.py --output OUTPUT_FILENAME
运行该代码段。 我的测试数据按键分组为:
('foo', [1, 5])
('bar', [5, 9, 2])
CombineFn将根据每个键的最小值和最大值返回:
('foo', [(1, 5)])
('bar', [(2, 9)])
join / cogroup的按键操作输出:
('foo', ([1, 5], [[(1, 5)]]))
('bar', ([5, 9, 2], [[(2, 9)]]))
归一化后:
('foo', [0.0, 1.0])
('bar', [0.42857142857142855, 1.0, 0.0])
这只是一个简单的测试,因此我确定可以针对提到的数据量对其进行优化,但它似乎可以作为起点。 考虑到可能需要进一步考虑(例如,如果min = max,请避免除以零)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.