список агрегирования Python в словарь - PullRequest
0 голосов
/ 04 июля 2018

У меня есть файл, который выглядит так -

Col1      Col2     Key       Value
101       a        f1        abc
101       a        f2        def
102       a        f2        xyz
102       a        f3        fgh
103       b        f1        rst

и мне нужен выходной файл, который выглядит следующим образом:

{"Col1":101, "Col2":"a", "kvpairs":{"f1":"abc","f2":"def"}}
{"Col1":102, "Col2":"a", "kvpairs":{"f2":"xyz","f3":"fgh"}}
{"Col1":103, "Col2":"b", "kvpairs":{"f1":"rst"}}

Я могу перебрать файл, объединяющий пары значений ключей для полей группировки Col1 и Col2 в список и помещая его в диктовку, но надеялся, что есть более питонический способ сделать это. Есть ответы на вопросы с использованием агрегации панд, но я не могу найти аккуратный (и эффективный способ) построения этой вложенной карты. Кроме того, исходный файл будет большим, например, 80-метровые записи в получающемся файле хрустят до 8 метров.

Я вижу, как загораются эти глаза :)

Ответы [ 3 ]

0 голосов
/ 04 июля 2018
data = []
for col1, col2, key, value in input:

    # look for an existing dict with col1 and col2
    for d in data:
        if d['col1'] == col1  and d['col2'] == col2:
            d['kvpairs'][key] = value
            break

    # no existing dict was found
    else:
        d.append({'col1': col1, 'col2': col2, 'kvpairs': {key: value}})

for d in data:
    print d
0 голосов
/ 04 июля 2018

groupby + agg + to_dict

df.groupby(["Col1", "Col2"])[["Key", "Value"]].agg(list).transform(lambda x: dict(zip(*x)),1).reset_index(name='kvpairs').to_dict('records')

[{'Col1': 101, 'Col2': 'a', 'kvpairs': {'f1': 'abc', 'f2': 'def'}},
 {'Col1': 102, 'Col2': 'a', 'kvpairs': {'f2': 'xyz', 'f3': 'fgh'}},
 {'Col1': 103, 'Col2': 'b', 'kvpairs': {'f1': 'rst'}}]

Если, конечно, df - это

z = io.StringIO("""Col1      Col2     Key       Value
101       a        f1        abc
101       a        f2        def
102       a        f2        xyz
102       a        f3        fgh
103       b        f1        rst""")

df = pd.read_table(z,delim_whitespace=True)

Объяснение

Сначала вы aggregate используете list

df.groupby(["Col1", "Col2"])[["Key", "Value"]].agg(list)

              Key           Value
Col1    Col2        
101     a    [f1, f2]     [abc, def]
102     a    [f2, f3]     [xyz, fgh]
103     b    [f1]         [rst]

Затем transform это вывод в словари и вообще переименование оси

.transform(lambda x: dict(zip(*x)),1).reset_index(name='kvpairs')

    Col1    Col2    kvpairs
0   101     a       {'f1': 'abc', 'f2': 'def'}
1   102     a       {'f2': 'xyz', 'f3': 'fgh'}
2   103     b       {'f1': 'rst'}

Наконец, используйте to_dict('records'), чтобы получить список словарей

.to_dict('records')
[{'Col1': 101, 'Col2': 'a', 'kvpairs': {'f1': 'abc', 'f2': 'def'}},
 {'Col1': 102, 'Col2': 'a', 'kvpairs': {'f2': 'xyz', 'f3': 'fgh'}},
 {'Col1': 103, 'Col2': 'b', 'kvpairs': {'f1': 'rst'}}]
0 голосов
/ 04 июля 2018

Использование itertools.groupby():

from itertools import groupby

for ((c1,c2),items) in groupby(lines, key=lambda x: x[:2]):
    d = {"Col1": c1, "Col2:": c2, "kvpairs":dict(x[2:] for x in items)}
    print(d)

Производит:

{'Col1': '101', 'Col2:': 'a', 'kvpairs': {'f1': 'abc', 'f2': 'def'}}
{'Col1': '102', 'Col2:': 'a', 'kvpairs': {'f2': 'xyz', 'f3': 'fgh'}}
{'Col1': '103', 'Col2:': 'b', 'kvpairs': {'f1': 'rst'}}

Похоже, вы анализируете некоторые значения для литералов - int, который вы можете сделать с int(c1), но я не уверен, как вы хотите справиться с превращением "a" в a.

(Предполагается, что у вас есть список итераций, возможно из модуля csv:)

lines = [
    ['101','a','f1','abc'],
    ['101','a','f2','def'],
    ['102','a','f2','xyz'],
    ['102','a','f3','fgh'],
    ['103','b','f1','rst']
]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...