Как объединить строки и добавить значения столбцов?

2

Поэтому у меня есть файл laaaaaaaarge:

Item|Cost1|Cost2
Pizza|50|25
Sugar|100|100
Spices|100|200
Pizza|100|25
Sugar|200|100
Pizza|50|100

Я хочу добавить все Cost1 и Cost2 для определенного элемента и создать объединенный вывод.

Я написал код python для этого,

item_dict = {}
for line in file:
    fields = line.split('|')
    item = fields[0]
    cost1 = fields[1]
    cost2 = fields[2]
    if item_dict.has_key(item):
        item_dict[item][0] += int(cost1)
        item_dict[item][1] += int(cost2)
    else:
        item_dict[item] = [int(cost1),int(cost2)]

for key, val in item_dict.items():
    print key,"|".join(val)

Есть ли способ сделать это очень эффективно и быстро в awk или с помощью любого другого волшебства?

Или я могу сделать свой python более элегантным и быстрым?

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

Pizza|200|150
Sugar|300|200
Spices|100|200
Теги:
awk

3 ответа

10
Лучший ответ

Что-то вроде этого...

$ awk 'BEGIN{OFS=FS="|"}
  NR>1 {cost1[$1]+=$2; cost2[$1]+=$3} 
  END{ for (i in cost1) print i, cost1[i], cost2[i]}' file
Sugar|300|200
Spices|100|200
Pizza|200|150

объяснение

  • BEGIN{OFS=FS="|"} устанавливает разделитель полей ввода и вывода как | ,
  • NR>1 означает, что мы будем делать некоторые действия для номера строки, большего, чем 1. Таким образом мы пропускаем заголовок.
  • cost1 и cost2 - это массивы, индекс которых является первым полем, а его значение представляет собой сумму до этой точки.
  • END {} - это то, что мы делаем после прочтения всего файла. Он состоит в прокрутке массива и печати значений.
  • 0
    Боже мой. Вы настоящий волшебник awk!
  • 0
    Хаха, спасибо, хотя в SO так много лучших волшебников :) Рад, что это сработало.
4
awk '
    BEGIN { FS=OFS="|" }
    NR==1 { expectedNF = NF; next }
    NF != expectedNF { print "Fix your #%@#&! data, idiot!"; exit 1 }'
    {
        items[$1]
        for (c=2;c<=NF;c++)
            cost[$1,c] += $c
    } 
    END {
        for (i in items) {
            printf "%s", i
            for (c=2;c<=NF;c++)
                printf "%s%s", OFS, cost[i,c]
            print ""
        }
    }
' file

Не стесняйтесь сжимать его на 1 или 2 линии по своему усмотрению.

  • 1
    +1, поскольку это обобщает решение на любое количество столбцов.
  • 2
    +1 Возможно, будет хорошей идеей прочитать первую строку и проверить ввод (т. Е. Прервать, если какая-либо строка не имеет правильного количества элементов), а не игнорировать заголовок.
Показать ещё 1 комментарий
1

На практике я бы сделал то, что сделал fedorqui. Для полноты, однако, этот скрипт python должен быть быстрее вашего оригинала:

#!/usr/bin/env python

import fileinput

item_dict = {}

for line in fileinput.input():
    if not fileinput.isfirstline():
        fields = line.strip().split('|')
        item = fields[0]
        cost1 = int(fields[1])
        cost2 = int(fields[2])
        try:
            item_dict[item][0] += cost1
            item_dict[item][1] += cost2
        except KeyError:
            item_dict[item] = [cost1, cost2]

for key, val in item_dict.items():
    print "%s|%s|%s" % (key,val[0],val[1])

Сохраните сценарий в файле, таком как sumcols и сделайте его исполняемым chmod +x sumcols и запустите как:

$ ./sumcols file
Spices|100|200
Sugar|300|200
Pizza|200|150

Ещё вопросы

Сообщество Overcoder
Наверх
Меню