Как посчитать значения между пустыми ячейками - PullRequest
6 голосов
/ 12 июля 2020

Я столкнулся с одной проблемой, которая больше меня. У меня есть 18 относительных больших текстовых файлов (около 30 тыс. Строк в каждом), и мне нужно подсчитать значения между пустыми ячейками во втором столбце. Вот простой пример моего файла:

Metabolism
line_1    10.2
line_2    10.1
line_3    10.3
TCA_cycle
line_4    10.7
line_5    10.8
Pyruvate_metab
line_6   100.8

На самом деле у меня около 500 строк описания (Metabolism, TCA_cycle, et c.), А диапазон строк составляет от нуля до нескольких сотен. .

Я хотел бы подсчитать значения для каждого блока (блок начинается с описания, а соответствующие строки всегда ниже), например

Metabolism  30.6
line_1    10.2
line_2    10.1
line_3    10.3
TCA_cycle   21.5
line_4    10.7
line_5    10.8
Pyruvate_metab 100.8
line_6   100.8

Или просто

30.3
21.5
100.8

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

Есть одна хитрость - это описания без строк с цифрами.

Transport
line_1000   100.1
line_1001   100.2
Cell_signal
Motility
Processing
Translation
line_1002   500.1
line_1003   200.2

И даже для этих строк и хотелось бы получить значение 0.

Transport     200.3
line_1000   100.1
line_1001   100.2
Cell_signal   0
Motility      0
Processing    0
Translation   700.3
line_1002   500.1
line_1003   200.2

Остальная часть файла выглядит одинаково и согласованно - 2 столбца, разделители табуляции, описания в первом столбце , значения во втором, без пробелов (только подчеркивание).

На самом деле у меня нет опыта работы с более сложным кодированием, поэтому я действительно не знаю, как решить эту проблему в командной строке. Я уже пробовал несколько способов Excel, но это было болезненно и безуспешно.

Ответы [ 3 ]

7 голосов
/ 12 июля 2020

С tac и любым awk:

tac file | awk 'NF==2{sum+=$2; print; next} {print $1 "\t" sum; sum=0}' | tac

С двумя улучшениями, предложенными квантур и Эд Мортон . См. Комментарии.

tac file | awk '($NF+0==$NF){sum+=$2; print; next} {print $1 "\t" sum+0; sum=0}' | tac

См .: 8 мощных встроенных переменных Awk - FS, OFS, RS, ORS, NR, NF, FILENAME, FNR

4 голосов
/ 12 июля 2020

Не могли бы вы попробовать следовать, написано и протестировано с показанными образцами в GNU awk.

awk '
FNR==NR{
  if($0!~/line/){  a[$0]; prev=$0 }
  else          {  a[prev]+=$NF   }
  next
}
!/line/{
  $0=$0 OFS (a[$0]?a[$0]:0)
}
1'  Input_file  Input_file

ИЛИ, если вы хотите вывод в красивой форме, добавьте column -t к приведенной выше команде, как показано ниже :

awk '
FNR==NR{
  if($0!~/line/){  a[$0]; prev=$0 }
  else          {  a[prev]+=$NF   }
  next
}
!/line/{
  $0=$0 OFS (a[$0]?a[$0]:0)
}
1'  Input_file  Input_file  |  column -t

Пояснение: Добавление подробного объяснения приведенного выше кода.

awk '                                           ##Starting awk program from here.
FNR==NR{                                        ##Checking FNR==NR which will be TRUE when Input_file is being read first time.
  if($0!~/line/){  a[$0]; prev=$0 }             ##checking condition if line contains string line and setting index of current line in a and setting prev value to current line.
  else          { a[prev]+=$NF    }             ##Else if line not starting from line then creating array a with index prev variable and keep on adding last field value to same index of array.
  next                                          ##next will skip all further statements from here.
}
!/line/{                                        ##Checking if current line doesnot have line keyword in it then do following.
  $0=$0 OFS (a[$0]?a[$0]:0)                     ##Re-creating current line with its current value then OFS(which is space by default) then either add value of a[$0] or 0 based on current line value is NOT NULL here.
}
1                                               ##Printing current line here.
' Input_file  Input_file                        ##Mentioning Input_file names here.
3 голосов
/ 13 июля 2020

Обычным awk:

awk '{
    if (NF == 1) {
        if (blockname)
            printf("%s\t%.2f\n%s", blockname, sum, lines)
        blockname = $0
        sum = 0
        lines=""
    } else if (NF == 2) {
        sum += $2 
        lines = lines $0 "\n"
    }
    next
}
END { printf("%s\t%.2f\n%s", blockname, sum, lines) }
' input.txt
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...