Python: Проверить, имеет ли следующая строка в CSV тот же идентификатор, что и значение в текущей строке? - PullRequest
0 голосов
/ 02 августа 2020

Я работаю над проектом по чтению данных о покупках из CSV и выводу JSON для полезной нагрузки API. Есть несколько строк с одним и тем же идентификатором заказа, так как каждая из них имеет отдельный элемент, и я хотел бы объединить элементы в один массив перед созданием полезной нагрузки для покупки.

order id             name    product_code   purchase_price
012006251700-68811   item1   321618         1380
012006251700-68811   item1   321618         690
012006241026-13750   item2   329452         1490
012006221101-40527   item3   326353         1990
012006221101-40527   item4   321625         1490
012006192158-63823   item5   323098         1990
012006192158-63823   item6   320923         590
012006192158-63823   item7   325051         590
012006192158-63823   item8   325446         1990

Я был могу импортировать строки из CSV, и я проверяю значение идентификатора текущей покупки, но не смог получить результаты, которые хотел бы получить.

Приведенный ниже код должен проверять, соответствует ли идентификатор значение такое же для следующей строки, и если да, добавьте только сведения об элементе в массив элементов.

Если следующая строка не имеет того же идентификатора, инструкция else добавит полную покупку к покупкам array.

import csv
import json

output = {'purchases': []}
items = {'items': []}
purchaseBody = {}
current_purchase = None

with open('tester - tester.csv') as csv_file:
    for purchase in csv.DictReader(csv_file):
        if current_purchase is not None and purchase['id'] == current_purchase['id']:
            items['items'].append({'id': purchase['id'],
                                  'name': purchase['name'],
                                  'product_code': purchase['product_code']
                                  'purchase_price': purchase['purchase_price'],
                                  })
                 
        else:

            purchaseBody = {
                'id': purchase['id'],
                'user': {'email': purchase['email']},
                'total': purchase['total'],
                'createdAt': purchase['createdAt']
                }
            items['items'].append({'id': purchase['id'],
                                  'name': purchase['name'],
                                  'product_code': purchase['product_code']
                                  'purchase_price': purchase['purchase_price'],
                                  })
            output['purchases'].append(purchaseBody)
            items = {'items': []}   
            purchaseBody.update(items)


        current_purchase = purchase


with open('file.json', 'w') as jsonfile:
    json.dump(output, jsonfile, ensure_ascii=False)
    jsonfile.write('\n')

Требуемый результат должен выглядеть примерно так:

{
    
    "purchases": [{
    
        "id": "purchase id",
    
        "user": {
    
            "email": "email"
    
        },
    
        "items": [{
    
                "id": "id1",
    
                "name": "name1",
    
                "additionalFields": {
    
                    "product_code": "product_code1",
    
                    "purchase_price": "purchase_price1"
    
                }
    
            },
    
            {
    
                "id": "id2",
    
                "name": "name2",
    
                "additionalFields": {
    
                    "product_code": "product_code2",
    
                    "purchase_price": "purchase_price2"
    
                }
    
            }
    
        ],
    
        "total": "total",
    
        "createdAt": "createdAt"
    
    }]
    
}

Ответы [ 2 ]

2 голосов
/ 02 августа 2020

В коде вопроса, я думаю, он сделает то, что вы хотите, сделав отступ строки

 current_purchase = purchase

так, чтобы она находилась внутри блока else.

Однако это вид задачи - итерация по коллекции и группировка по ключу - можно упростить с помощью функции itertools.groupby . Учитывая отсортированную коллекцию, он выполнит группировку за вас. Функцию operator.itemgetter можно использовать для уменьшения объема кода, необходимого для получения значений из словарей строк.

import csv
import itertools
import operator
import json

output = {'purchases': []}

reader = csv.DictReader(buf)

# Sort the rows by `id` - if the data is not guaranteed to be sorted.
# If the order id guaranteed, pass `reader` to itertools.groupby.
keyfunc = operator.itemgetter('id')
rows = sorted(reader, key=keyfunc)

# Make a function to build the item dictionaries.
item_keys = ('id', 'name')
item_values = operator.itemgetter(*item_keys)
additional_keys = ('product_code', 'purchase_price')
additional_values = operator.itemgetter(*additional_keys)


def build_item(purchase):
    item = dict(zip(item_keys, item_values(purchase)))
    item['additionalFields'] = dict(zip(additional_keys, additional_values(purchase)))
    return item


for _, purchases in itertools.groupby(rows, keyfunc):
    # Get the first row, because we need some of the data to build purchaseBody.
    purchase = next(purchases)
    # Initialise the items dict with data from the first purchase, and add the rest.
    items = [build_item(purchase)]
    items.extend(build_item(purchase) for p in purchases)
    purchaseBody = {
        'id': purchase['id'],
        'user': {'email': purchase['email']},
        'total': sum(float(item['additionalFields']['purchase_price']) for item in items),
        'createdAt': '2020-08-02',
        'items': items,
    }
    output['purchases'].append(purchaseBody)

with open('file.json', 'w') as jsonfile:
    json.dump(output, jsonfile, ensure_ascii=False)
    jsonfile.write('\n')
2 голосов
/ 02 августа 2020
  • Я бы подумал об использовании pandas
  • Используйте .groupby для выбора групп
    • Когда .groupby выполняется для одного столбца, группа возвращается как str, а если .groupby выполняется для нескольких столбцов, возвращается tuple.
    • o_id - это str который представляет собой значение, используемое для groupby
    • o_id должно быть list или tuple, чтобы zip с groupby_list создавало dict.
    • d - это фрейм данных для каждой группы groupby.
  • Используйте .iterrows для перебора строк каждой группы
    • Возвращает index, представленное первым _, потому что это не нужно
    • Возвращает data, из которого отбрасываются метки в groupby_list, затем преобразует остаток в dict с помощью .to_dict(), и добавьте его к list, att_list
    • После цикла по всем строкам группы назначьте items_list в качестве значения group['items']
  • После прохождения каждой группы с полным рейтингом, добавьте dict, group, к dict_list.
  • dict_list можно преобразовать обратно в фрейм данных со следующим:
    • df = pd.json_normalize(dict_list, 'items', meta=groupby_list)

Ожидаемый результат

{'items': [{'name': 'item6', 'product_code': '323098', 'purchase_price': 1990},
           {'name': 'item7', 'product_code': '3209233', 'purchase_price': 590}],
 'orderId': '012006192158-63823'}

Код для получения ожидаемого результата

import pandas as pd
import json

# read in the file
df = pd.read_csv('test.csv')

dict_list = list()
groupby_list = ['order id']

for o_id, d in df.groupby(groupby_list):
    if type(o_id) != tuple:
        o_id = [o_id]
    group = dict(zip(groupby_list, o_id))
    items_list = list()
    for _, data in d.iterrows():
        data = data.drop(labels=groupby_list)
        items_list.append(data.to_dict())
    group['items'] = items_list
    dict_list.append(group)

# save to a file
with open('test.json', 'w') as f:
    json.dump(dict_list, f, ensure_ascii=False)
    jsonfile.write('\n')

Конечный результат: dict_list

[{
        'items': [{
                'name': 'item6',
                'product_code': 323098,
                'purchase_price': 1990
            }, {
                'name': 'item7',
                'product_code': 320923,
                'purchase_price': 590
            }, {
                'name': 'item8',
                'product_code': 325051,
                'purchase_price': 590
            }, {
                'name': 'item9',
                'product_code': 325446,
                'purchase_price': 1990
            }
        ],
        'order id': '012006192158-63823'
    }, {
        'items': [{
                'name': 'item4',
                'product_code': 326353,
                'purchase_price': 1990
            }, {
                'name': 'item5',
                'product_code': 321625,
                'purchase_price': 1490
            }
        ],
        'order id': '012006221101-40527'
    }, {
        'items': [{
                'name': 'item3',
                'product_code': 329452,
                'purchase_price': 1490
            }
        ],
        'order id': '012006241026-13750'
    }, {
        'items': [{
                'name': 'item1',
                'product_code': 321618,
                'purchase_price': 1380
            }, {
                'name': 'item2',
                'product_code': 321618,
                'purchase_price': 690
            }
        ],
        'order id': '012006251700-68811'
    }
]

test.csv

order id,name,product_code,purchase_price
012006251700-68811,item1,321618,1380
012006251700-68811,item2,321618,690
012006241026-13750,item3,329452,1490
012006221101-40527,item4,326353,1990
012006221101-40527,item5,321625,1490
012006192158-63823,item6,323098,1990
012006192158-63823,item7,320923,590
012006192158-63823,item8,325051,590
012006192158-63823,item9,325446,1990
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...