Python 中的展平实体属性值 (EAV) 架构
Flatten Entity-Attribute-Value (EAV) Schema in Python
我有一个实体-属性-值格式的 csv 文件(即,我的 event_id
是非唯一的并重复 k 次 k 关联属性):
event_id, attribute_id, value
1, 1, a
1, 2, b
1, 3, c
2, 1, a
2, 2, b
2, 3, c
2, 4, d
是否有任何方便的技巧可以将可变数量的属性(即、行)转换为列?这里的关键是输出应该是结构化数据的 m x n table,其中 m = max(k);用 NULL
填充缺失的属性是最佳的:
event_id, 1, 2, 3, 4
1, a, b, c, null
2, a, b, c, d
我的计划是 (1) 将 csv 转换为 JSON 对象,如下所示:
data = [{'value': 'a', 'id': '1', 'event_id': '1', 'attribute_id': '1'},
{'value': 'b', 'id': '2', 'event_id': '1', 'attribute_id': '2'},
{'value': 'a', 'id': '3', 'event_id': '2', 'attribute_id': '1'},
{'value': 'b', 'id': '4', 'event_id': '2', 'attribute_id': '2'},
{'value': 'c', 'id': '5', 'event_id': '2', 'attribute_id': '3'},
{'value': 'd', 'id': '6', 'event_id': '2', 'attribute_id': '4'}]
(2) 提取唯一事件 ID:
events = set()
for item in data:
events.add(item['event_id'])
(3) 创建一个列表列表,其中每个内部列表都是相应父事件的属性列表。
attributes = [[k['value'] for k in j] for i, j in groupby(data, key=lambda x: x['event_id'])]
(4) 创建一个将事件和属性放在一起的字典:
event_dict = dict(zip(events, attributes))
看起来像这样:
{'1': ['a', 'b'], '2': ['a', 'b', 'c', 'd']}
我不确定如何使所有内部列表的长度相同,并在必要时填充 NULL
值。这似乎是步骤(3)中需要完成的事情。此外,创建充满 m NULL
值的 n 列表已经在我脑海中浮现,然后遍历每个列表并使用 attribute_id
作为列表位置;但这似乎很糟糕。
你的基本想法似乎是正确的,但我会按如下方式实现它:
import itertools
import csv
events = {} # we're going to keep track of the events we read in
with open('path/to/input') as infile:
for event, _att, val in csv.reader(infile):
if event not in events:
events[event] = []
events[int(event)].append(val) # track all the values for this event
maxAtts = max(len(v) for _k,v in events.items()) # the maximum number of attributes for any event
with open('path/to/output', 'w') as outfile):
writer = csv.writer(outfile)
writer.writerow(["event_id"] + list(range(1, maxAtts+1))) # write out the header row
for k in sorted(events): # let's look at the events in sorted order
writer.writerow([k] + events[k] + ['null']*(maxAtts-len(events[k]))) # write out the event id, all the values for that event, and pad with "null" for any attributes without values
我有一个实体-属性-值格式的 csv 文件(即,我的 event_id
是非唯一的并重复 k 次 k 关联属性):
event_id, attribute_id, value
1, 1, a
1, 2, b
1, 3, c
2, 1, a
2, 2, b
2, 3, c
2, 4, d
是否有任何方便的技巧可以将可变数量的属性(即、行)转换为列?这里的关键是输出应该是结构化数据的 m x n table,其中 m = max(k);用 NULL
填充缺失的属性是最佳的:
event_id, 1, 2, 3, 4
1, a, b, c, null
2, a, b, c, d
我的计划是 (1) 将 csv 转换为 JSON 对象,如下所示:
data = [{'value': 'a', 'id': '1', 'event_id': '1', 'attribute_id': '1'},
{'value': 'b', 'id': '2', 'event_id': '1', 'attribute_id': '2'},
{'value': 'a', 'id': '3', 'event_id': '2', 'attribute_id': '1'},
{'value': 'b', 'id': '4', 'event_id': '2', 'attribute_id': '2'},
{'value': 'c', 'id': '5', 'event_id': '2', 'attribute_id': '3'},
{'value': 'd', 'id': '6', 'event_id': '2', 'attribute_id': '4'}]
(2) 提取唯一事件 ID:
events = set()
for item in data:
events.add(item['event_id'])
(3) 创建一个列表列表,其中每个内部列表都是相应父事件的属性列表。
attributes = [[k['value'] for k in j] for i, j in groupby(data, key=lambda x: x['event_id'])]
(4) 创建一个将事件和属性放在一起的字典:
event_dict = dict(zip(events, attributes))
看起来像这样:
{'1': ['a', 'b'], '2': ['a', 'b', 'c', 'd']}
我不确定如何使所有内部列表的长度相同,并在必要时填充 NULL
值。这似乎是步骤(3)中需要完成的事情。此外,创建充满 m NULL
值的 n 列表已经在我脑海中浮现,然后遍历每个列表并使用 attribute_id
作为列表位置;但这似乎很糟糕。
你的基本想法似乎是正确的,但我会按如下方式实现它:
import itertools
import csv
events = {} # we're going to keep track of the events we read in
with open('path/to/input') as infile:
for event, _att, val in csv.reader(infile):
if event not in events:
events[event] = []
events[int(event)].append(val) # track all the values for this event
maxAtts = max(len(v) for _k,v in events.items()) # the maximum number of attributes for any event
with open('path/to/output', 'w') as outfile):
writer = csv.writer(outfile)
writer.writerow(["event_id"] + list(range(1, maxAtts+1))) # write out the header row
for k in sorted(events): # let's look at the events in sorted order
writer.writerow([k] + events[k] + ['null']*(maxAtts-len(events[k]))) # write out the event id, all the values for that event, and pad with "null" for any attributes without values