I am trying to perform a de-noramlization operation, where I need to reorganize a table with the following logic:
| itemid | class | value |
+--------+-------+-------+
| 1 | A | 0.2 | | itemid | value A | value B | value C |
| 1 | B | 10.3 | ==> +--------+---------+---------+---------+
| 2 | A | 3.0 | ==> | 1 | 0.2 | 10.3 | |
| 2 | B | 0.2 | ==> | 2 | 3.0 | 0.2 | |
| 3 | A | 0.0 | | 3 | 0.0 | 1.2 | 5.4 |
| 3 | B | 1.2 |
| 3 | C | 5.4 |
My approach has been to execute a for loop in order to filter by class, given that I know the list of classes a-priori and then to join the resulting pcollections.
The high-level code:
CLASSES = ["A", "B", "C"]
tables = [
(
data
| "Filter by Language" >> beam.Filter(lambda elem: elem["class"]==c)
| "Add id as key" >> beam.Map(lambda elem: (elem["itemid"], elem))
)
for cin CLASSES
]
and the join:
_ = (
tables
| "Flatten" >> beam.Flatten()
| "Join Collections" >> beam.GroupByKey()
| "Remove key" >> beam.MapTuple(lambda _, val: val)
| "Merge dicts" >> beam.ParDo(mergeDicts())
| "Write to GCS" >> beam.io.WriteToText(output_file)
)
with (edit following Peter Kim's suggestion):
class mergeDicts(beam.DoFn):
process(self, elements):
result = {}
for dictionary in elements:
if len(dictionary)>0:
result["itemid"] = dictionary["itemid"]
result["value {}".format(dictionary["class"])] = dictionary["value"]
yield result
My issue here is that when the pipeline is executed within the Apache Beam compute engine, I obtain identical pcollections filtered by the last element of the list, which in this case is C.
[ADDED] It looks like the Apache Beam engine takes the iterating variable in its final state, which means the last element of the iterated list, for all the invoked branches.
I am obviously following the wrong approach, but which should be the best way to perform this operation?