Как добавить столбец с процентом

Я хотел бы рассчитать процент значения в каждой строке из всех строк и добавить его в качестве другого столбца. Вход (разделитель:\t):

1   10      
2   10
3   20
4   40

Желаемый вывод с добавленным третьим столбцом, показывающим вычисляемый процент, основанный на значениях во втором столбце:

1   10   12.50   
2   10   12.50
3   20   25.00
4   40   50.00

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

Ответ 1

Здесь вы идете, один шаг pass шаг awk-решение -

awk 'NR==FNR{a = a + $2;next} {c = ($2/a)*100;print $1,$2,c }' file file

[jaypal:~/Temp] cat file
1   10      
2   10
3   20
4   40
[jaypal:~/Temp] awk 'NR==FNR{a = a + $2;next} {c = ($2/a)*100;print $1,$2,c }' file file
1 10 12.5
2 10 12.5
3 20 25
4 40 50

Обновление: Если вкладка является обязательной для вывода, просто установите для переменной OFS значение "\ t".

[jaypal:~/Temp] awk -v OFS="\t" 'NR==FNR{a = a + $2;next} {c = ($2/a)*100;print $1,$2,c }' file file
1   10  12.5
2   10  12.5
3   20  25
4   40  50

Прорыв операторов шаблона {action}:

  • Первый шаблон - NR==FNR. FNR - это встроенная переменная awk, которая отслеживает количество записей (по умолчанию разделенных новой строкой) в заданном файле. Таким образом, FNR в нашем случае будет 4. NR похож на FNR, но он не получает reset до 0. Он продолжает расти. Таким образом, NR в нашем случае будет 8.

  • Этот шаблон будет действителен только для первых 4 записей, и именно это мы хотим. После просмотра через 4 записи мы присваиваем общее значение переменной a. Обратите внимание, что мы не инициализировали его. В awk нам этого не нужно. Тем не менее, это сломается, если весь столбец 2 равен 0. Таким образом, вы можете справиться с этим, поставив оператор if во втором операторе действия. Если это деление, только если a > 0 else сказать деление на 0 или что-то еще.

  • next необходимо, потому что мы действительно не хотим, чтобы выполнялся второй оператор {action}. next указывает awk прекратить дальнейшие действия и перейти к следующей записи.

  • После анализа четырех записей начинается следующий шаблон {действие}, который довольно прост. Выполнение процента и печать столбцов 1 и 2 вместе с процентом рядом с ними.

Примечание.. Как указано в комментарии @lhf, этот однострочный лайнер будет работать только до тех пор, пока у вас есть данные, установленные в файле. Он не будет работать, если вы передадите данные через трубу.

В комментариях обсуждается, как сделать этот awk one-liner ввод данных из pipe вместо file. Ну, единственный способ, которым я мог подумать, - сохранить значения столбца в array, а затем использовать for loop, чтобы выплюнуть каждое значение вместе с их процентом.

Теперь arrays в awk являются associative и никогда не находятся в порядке, то есть вытягивание значений из массивов не будет в том же порядке, в каком они вошли. Так что если это нормально, лайнер должен работать.

[jaypal:~/Temp] cat file
1   10      
2   10
3   20
4   40

[jaypal:~/Temp] cat file | awk '{b[$1]=$2;sum=sum+$2} END{for (i in b) print i,b[i],(b[i]/sum)*100}'
2 10 12.5
3 20 25
4 40 50
1 10 12.5

Чтобы привести их в порядок, вы можете передать результат на sort.

[jaypal:~/Temp] cat file | awk '{b[$1]=$2;sum=sum+$2} END{for (i in b) print i,b[i],(b[i]/sum)*100}' | sort -n
1 10 12.5
2 10 12.5
3 20 25
4 40 50

Ответ 2

Вы можете сделать это за пару проходов

#!/bin/bash

total=$(awk '{total=total+$2}END{print total}' file)
awk -v total=$total '{ printf ("%s\t%s\t%.2f\n", $1, $2, ($2/total)*100)}' file

Ответ 3

Вам нужно избегать его как %%. Например:

printf("%s\t%s\t%s%%\n", $1, $2, $3)

Ответ 4

Возможно, есть лучший способ, но я бы дважды передал файл.

Содержимое 'infile':

1       10 
2       10
3       20
4       40

Содержимое 'script.awk':

BEGIN {
        ## Tab as field separator.
        FS = "\t";
}

## First pass of input file. Get total from second field.
ARGIND == 1 {
        total += $2;
        next;
}

## Second pass of input file. Print each original line and percentage as third field.
{
        printf( "%s\t%2.2f\n", $0, $2 * 100 / total );
}

Запустите script в моем linux окне:

gawk -f script.awk infile infile

И результат:

1       10      12.50
2       10      12.50
3       20      25.00
4       40      50.00