Транспонируйте файл и замените отсутствующие значения

Я полагаю, что можно использовать gdb сделать это. См. несколько из ответов на этот stackoverflow вопрос. Кажется, что это могло бы прибыть из этой статьи блога.

5
23.04.2015, 11:14
2 ответа

Если вы действительно хотите сделать это обычным sed/awk, это действительно возможно:

Как упоминал Joe, использование SPACE в качестве разделителя полей & данных является проблемой в awk.

Поэтому я предлагаю использовать sed, чтобы сначала переформатировать данные:

sed 's/ *$//' удаляет SPACEы в конце строки (все строки ввода, кроме первой, заканчиваются SPACE, поэтому это стандартизирует ввод и удаляет потенциальные пропущенные значения в конце каждой строки).

Далее, sed 's/ / . /g/' вставляет . между каждой парой соседних ПРОБЕЛов (заполняя потенциальные пропущенные значения, которые не находятся в конце строки).

Так как при этом будут вставлены дополнительные ПРОБЕЛЫв случае смежных пропущенных значений, sed 's/ / /g' должен быть использован для их удаления.

Затем, awk может использовать первую (т.е. заголовок) строку, чтобы узнать имена и номера показаний, добавить потенциально недостающие значения в конец каждой строки (обо всех остальных уже позаботился sed), суммировать и подсчитать все показания, отслеживая соответствующее имя и инструмент, и вывести средства (если они есть) в нужной ориентации/порядке:

sed -e 's/ *$//' -e 's/  / . /g' -e 's/  / /g' <<< 'Name Instrument Rep R1 R2 R3
N1 I1 1 1 2 3
N2 I1 1 1 3 4
N1 I1 2 2 3 4
N3 I1 2 3 4 5
N1 I2 1 1 2 3
N2 I2 1 1 3 4
N2 I2 2 2 3 4
N3 I2 1 3 4 5
N1 I3 1 1   4
N2 I3 1 2 5
N3 I3 1   6
N3 I3 2     1' | awk '

# get number of readings/fields
NR==1{for(i=4;i<=NF;++i)readings[i-4]=$i;fields=NF;next}

# add missing fields in the end
{for(i=NF+1;i<=fields;++i)$i="."}

# keep track of names & instruments
names[$1];instruments[$2]

# sum & count readings per name/instrument (ignoring missing ["."] values)
{for(i=4;i<=NF;++i)if($i!="."){sum[readings[i-4] FS $2 FS $1]+=$i;++count[readings[i-4] FS $2 FS $1]}}

# after reading all data:
END{

  # print header
  printf "Reading"FS"Instrument";for(name in names)printf FS name;print ""

  # sort output rows by instrument
  for(instrument in instruments){

    # keep order of readings
    for(i=0;i<length(readings);++i){

      # print first two columns
      printf readings[i] FS instrument

      # remaining columns (i.e. names):
      for(name in names){

        # if data available:
        if(count[readings[i] FS instrument FS name]){

          # print average
          printf FS sum[readings[i] FS instrument FS name]/count[readings[i] FS instrument FS name]

        # otherwise:
        }else{

          # print missing value ["."]
          printf FS "."
        }

      # proceed with next row
      }print ""
    }
  }
}
'

Примечание: На мой взгляд, использование FS в качестве разделителя в индексах многомерных массивов является лучшим выбором в большинстве случаев, поскольку все поля гарантированно не содержат его (в случае, если вам придется итерировать массив и разделять "размеры" индексов). Хотя здесь это не нужно, я сделал это привычкой.

edit: Джо указал, что способ учета имен/инструментов в предыдущей версии этого ответа может потребовать дополнительных пояснений. Это послужило основой для упрощенной версии, использованной выше: В отличие от k в a, который проверяет существование ключа k в массиве a не создавая такой записи, a[k] будет присваивать пустое значение этой записи (и возвращать его).

Для меня приведенный выше код выводит то, что вы просили:

Reading Instrument N1 N2 N3
R1 I1 1.5 1 3
R2 I1 2.5 3 4
R3 I1 3.5 4 5
R1 I2 1 1.5 3
R2 I2 2 3 4
R3 I2 3 4 5
R1 I3 1 2 .
R2 I3 . 5 6
R3 I3 4 . 1

Примечание: Синтаксис <<<, который я использую, представляет собой HERE-STRING и может работать не во всех оболочках (bash поддерживает его, однако). Просто передайте путь к входному файлу в sed, и он должен работать во всех оболочках (насколько я знаю).

Примечание: Это будет работать, только если все ваши данные помещаются в памяти. Если это не так, должно существовать менее требовательное к памяти решение для суммирования данных, основанное на сортировке входных данных. Транспонирование матрицы в этом случае может быть более сложным.

edit:

Примечание: Мой вывод не содержит ПРОБЕЛ в конце строки, в отличие от вашего примера, так как я не смог понять, когда вы ставите ПРОБЕЛ, а когда нет. Если это имеет какое-то значение, пожалуйста, скорректируйте вопрос, и я обновлю ответ соответствующим образом. В противном случае, подумайте о том, чтобы убрать эти ПРОБЕЛЫ из ожидаемого результата.

3
27.01.2020, 20:39

Вот непосредственные проблемы:

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

Для начала при таком подходе у вас есть $0, который содержит всю строку ввода. К показаниям можно добраться с помощью подстроки ($0, offset, 1), где смещение 7, 9,11 или 13 (я забываю, если индексация начинается с 0 или 1. Если 0, то вычитайте 1 из каждого смещения).

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

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

Можно использовать gsub для замены всех вхождений двух последовательных пробелов, за которыми следует либо третья пустая строка, либо конец строки на " M " или " 0 ".

В вашем текущем 1-ом окне вы должны будете проверить, что пропущено, прежде чем увеличивать и суммировать.

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

Я думаю, что понимаю, что делает ваш первый awk, но я понятия не имею, что вы пытаетесь сделать со вторым.

3) Вас могут заставить использовать точку для недостающего значения, чтобы успокоить какую-нибудь другую программу, которой вы собираетесь скормить этот вывод, но в общем, это плохая идея, потому что она выглядит как десятичная точка (что законно в ваших данных) и может быть интерпретирована какой-нибудь программой как нулевое значение или вообще сделать другой анализ более хитрым.

2
27.01.2020, 20:39

Теги

Похожие вопросы