Как сохранить первое, минимальное, максимальное значение из поля, разделенного точкой с запятой, в таблице? - PullRequest
0 голосов
/ 04 августа 2020

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

Name First Last
foo3;foo3;foo3;foo3;foo3    11869;12010;12179;12613;12613   12227;12057;12227;12721;12697
bar10;bar10;bar10   14404;15005;15796   14501;15038;15947
locM;locM;locM;locM 29554;30267;30564;30976 30039;30667;30667;31109

Я хотел бы сделать следующее [в BASH или R] с этим файлом:

(1) В первом столбце оставьте только одну запись.

(2) Во втором столбце оставьте только наименьшее значение из списка чисел, разделенных точкой с запятой. .

(3) В третьем столбце оставьте только максимальное значение из списка чисел, разделенных точкой с запятой.

Вот желаемый результат:

Name First Last
foo3    11869   12721
bar10   14404   15947
locM    29554   31109

Следует отметить, что максимальное и минимальное значения не всегда первое и последнее значения в списках, разделенных точкой с запятой, соответственно.

Обновление (любительские идеи для решения проблемы):

  • По адресу (1), я считаю, что что-то вроде awk -F";" 'BEGIN{ORS="/t"} !seen[$1]++' input.txt > output.txt было бы полезно для удаления повторяющихся экземпляров, однако я не понял, как это написать полностью.
  • По адресу (2), я считаю, что что-то похожее на cut -f2 -d"/t" input.txt > output.txt | cut -f1 -d";" | sort -n | head -1 сможет выберите минимальное число в списке, разделенном точкой с запятой.
  • Для адреса (3), я полагаю, что-то похожее на cut -f2 -d"/t" input.txt > output.txt | cut -f1 -d";" | sort -n | tail -1 сможет выбрать максимальное число в списке, разделенном точкой с запятой.

Ответы [ 3 ]

4 голосов
/ 05 августа 2020
$ cat tst.awk
BEGIN { FS=OFS="\t" }
NR == 1 { print; next }
{ print val($1,1), val($2,"min"), val($3,"max") }

function val(str,type,  vals,min,max) {
    split(str,vals,/;/)
    min = max = vals[1]
    for (i in vals) {
        if (vals[i] < min) {
            min = vals[i]
        }
        if (vals[i] > max) {
            max = vals[i]
        }
    }
    if      ( type == "min" ) return min
    else if ( type == "max" ) return max
    else                      return vals[type]
}

.

$ awk -f tst.awk file
Name    First   Last
foo3    11869   12721
bar10   14404   15947
locM    29554   31109

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

3 голосов
/ 04 августа 2020

Не могли бы вы попробовать следующее, написанное и протестированное только на основе показанных вами образцов.

awk '
{
  split($1,array,";")
  num1=split($2,array1,";")
  num2=split($3,array2,";")
  min=array1[1]
  for(i=2;i<=num1;i++){
    min=(min<array1[i]?min:array1[i])
  }
  max=array2[1]
  for(i=2;i<=num2;i++){
    max=(max>array2[i]?max:array2[i])
  }
  print array[1],min,max
}'  Input_file

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

awk '                                                        ##Starting awk program from here.
{
  split($1,array,";")                                        ##Splitting 1st field into array with separator as ; here.
  num1=split($2,array1,";")                                  ##Splitting 2nd field into array1 with separator as ; here.
  num2=split($3,array2,";")                                  ##Splitting 3rd field into array2 with separator as ; here.
  for(i=1;i<=num1;i++){                                      ##Running for loop till vale of num1 which is total elements in array1.
    min=(min<array1[i]?(min?min:array1[i]):array1[i])        ##Creating min here which is using ternary operator to check if current element of array1 is lesser than min then keep it else keep min current value and so on comparing each element one by one here.
  }
  for(i=1;i<=num2;i++){                                      ##Running for loop till value of mun2 which is total elements in array2.
    max=(max>array2[i]?max:array2[i])                        ##Creating max here using ternary operator checks if max value greater than array2 current value then keep it as it is else assign current value of array2 to max here.
  }
  print array[1],min,max                                     ##Printing 1st element of array then min and max here.
  min=max=""                                                 ##Nullifying variables min and max here.
}'  Input_file                                               ##Mentioning Input_file names here.
2 голосов
/ 06 августа 2020

Это может сработать для вас (GNU sed и Bash):

sed -E '1b;s/([^;]*).*\t(.*)\t(.*)/echo "\1\t$(echo "\2"|tr ";" "\\n"|sort -n |head -1)\t$(echo "\3"|tr ";" "\\n"|sort -n |tail -1)"/e' file

Не обрабатывать заголовки.

Разделите каждую строку данных на три столбца, разделенных табуляцией (поля внутри столбцов разделены ;):

  1. Уменьшить первый столбец до первого поля этого столбца.
  2. Сортировать второй столбец численно и оставить только первое поле, т. е. минимум .
  3. Сортировать третий столбец численно и оставить только последнее поле, т.е. максимум.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...