Разбить столбец на два столбца на основе разделителя в CSV-файле с помощью Awk? - PullRequest
1 голос
/ 27 сентября 2019

Я новичок в сценариях оболочки.У меня есть CSV-файл "a.csv" с 15000 строк и 15 столбцов.В нем есть один столбец с именем "id_data" .

В файле .csv всегда указан столбец 3.

Значения в этом наборе данных выглядят следующим образом:

A,B,id_data,C,D,E,F,...
a,b,12345_85485,c,d,e,f,...
a,b,45786_456322,c,d,e,f,...
a,b,12345_325489,c,d,e,f,...
a,b,_45230_14693,c,d,e,f....

, где данные столбцов в диапазоне от чисел до даты, строки, ссылки.

Я хочу подсчитать уникальные значения в столбце 3 после символа '_' .

Для этого я хочу разбить этот столбец на основе числа до _ и числа после _ на два разных столбца, а именно: «Данные ID1» и «Данные ID2».Разделение не обязательно, хотя.Основной целью является вычисление уникальных чисел после '_' в столбце 3.

Вывод должен выглядеть следующим образом в файле csv:

A,B,ID1 Data,ID2 Data,D,E,F...
a,b,12345,85485,d,e,f,...
a,b,45786,456322,d,e,f,...
a,b,12345,325489,d,e,f,...
a,b,45230,4693,d,e,f,...

Iпопытался сделать это с помощью следующей команды:

cat a.csv | sed -Ee 's/(.*)id_data/\1ID1 Data ID2 Data/' -e 's/,[_ ]/,/' -e 's/_/,/'

Он напечатал вывод в соответствии с требованием.Но нет никаких изменений в столбцах файла CSV.Напечатанная o / p выглядит как a, b, 12345,85485, c, d, e, f, ... .

Как применить изменение к столбцам в файле csv?Любые советы будут полезны.Спасибо

* Обратите внимание, что в начале может быть место или даже '_'.

Ответы [ 5 ]

1 голос
/ 27 сентября 2019

Не могли бы вы попробовать следующее, это универсальное решение, где нам не нужно жестко кодировать значение поля в решении, оно будет искать строку id_data, в первой строке получает номер столбца и действует соответственно для остальных строк.of Input_file.

awk 'FNR==1{for(j=1;j<=NF;j++){if($j=="id_data"){field=j;$j="ID1_data ID2_data"}};print;next} {sub(/^_/,"",$field); sub(/_/,OFS,$field)} 1' Input_file

Добавление формы решения, не относящейся к одному вкладышу:

awk '
FNR==1{
  for(j=1;j<=NF;j++){
    if($j=="id_data"){
      field=j
      $j="ID1_data  ID2_data"
    }    
  }
  print
  next
}
{
  sub(/^_/,"",$field)
  sub(/_/,OFS,$field)
}
1
'  Input_file

ПРИМЕЧАНИЕ: Пожалуйста, добавьте BEGIN{FS=OFS=","} в коде выше, если ваш Input_file разделен запятой.

Объяснение:

awk '                                ##Starting awk program here.
FNR==1{                              ##Checking condition if FNR==1 which will be TRUE for 1st line.
  for(j=1;j<=NF;j++){                ##Started a for loop from j=1 to till value of NF here.
    if($j=="id_data"){               ##Checking condition if current field is equal to id_data then do following.
      field=j                        ##Setting field variable value to variable j value.
      $j="ID1_data  ID2_data"        ##Setting current field value to string ID1_data space ID2_data here.
    }
  }
  print                              ##Printing current line.
  next                               ##next will skip all further statements from here.
}
{
  sub(/^_/,"",$field)                ##Using substitute function to substitute starting _ with NULL in $field, where field is a variable set in first line.
  sub(/_/,OFS,$field)                ##Using substitute function to substitute  _ with OFS for $field field.
}
1                                    ##Mentioning 1 will print edited/non-edited lines.
'  Input_file                        ##Mentioning Input_file name here.


РЕДАКТИРОВАТЬ: Согласно комментарию OP:

awk '
BEGIN{
  FS=OFS=","
}
FNR==1{
  for(j=1;j<=NF;j++){
    if($j=="id_data"){
      field=j
      $j="ID1_data  ID2_data"
    }    
  }
  print
  next
}
{
  sub(/^_/,"",$field)
  sub(/.*_/,OFS,$field)
}
1
'  Input_file
0 голосов
/ 28 сентября 2019

Я добавляю решение, не основанное на awk.

Использование Миллера (https://github.com/johnkerl/miller) и начиная с

A,B,id_data,C,D,E,F,...
a,b,12345_85485,c,d,e,f,...
a,b,45786_456322,c,d,e,f,...
a,b,12345_325489,c,d,e,f,...
a,b,_45230_14693,c,d,e,f....

, а затем запуск

mlr --csv --ragged cut -f id_data then nest --explode --values --across-records --nested-fs "_" -f id_data then skip-trivial-records then uniq -a -c input.csv >output.csv

у вас будет

count,id_data
2,12345
1,85485
1,45786
1,456322
1,325489
1,45230
1,14693

Если вы хотите получить ответ на свой вопрос, вы можете запустить

mlr --csv --ragged put -S '$id_data=gsub($id_data,"^([^-0-9]+)([0-9])(.+)","\2\3")' then nest --explode --values --across-fields --nested-fs "_" -f id_data then unsparsify input.csv  >output.csv

и получить

A,B,id_data_1,id_data_2,C,D,E,F,...
a,b,12345,85485,c,d,e,f,...
a,b,45786,456322,c,d,e,f,...
a,b,12345,325489,c,d,e,f,...
a,b,45230,14693,c,d,e,f....,
0 голосов
/ 27 сентября 2019
$ awk 'BEGIN{FS=OFS=","} {n=split($3,f,/[ _]/); $3=(NR>1 ? f[n-1] OFS f[n] : "ID1 Data" OFS "ID2 Data")} 1' file
A,B,ID1 Data,ID2 Data,C,D,E,F,...
a,b,12345,85485,c,d,e,f,...
a,b,45786,456322,c,d,e,f,...
a,b,12345,325489,c,d,e,f,...
a,b,45230,14693,c,d,e,f....
0 голосов
/ 27 сентября 2019

Если имеется более 2 столбцов (количество столбцов n)

cat file
id_data
12345_85485_243524_435
45786_456322_2435_2345
 12345_325489_2435_45
_45230_14693_2345_453

Тогда это может сделать:

awk -F'_' 'NR>1{sub(/^ /,"");for (i=($1?1:2);i<=NF;i++) printf "%s\t\t",$i;print ""}' file
12345           85485           243524          435
45786           456322          2435            2345
12345           325489          2435            45
45230           14693           2345            453
0 голосов
/ 27 сентября 2019

Вы можете сделать что-то вроде этого:

awk -F'_' -v OFS='\t' '{sub(/^[_ ]/,"");$1=$1}1' file
id      data
12345   85485
45786   456322
12345   325489
45230   14693

Форматирование заголовка

awk -F'_' -v OFS='\t\t' 'NR==1 {print $1"1 "$2"\t"$1"2 "$2;next}{sub(/^[_ ]/,"");$1=$1}1' file
id1 data        id2 data
12345           85485
45786           456322
12345           325489
45230           14693

Это основано на решении Pacifists, чтобы он мог видеть, что необходимо исправить, чтобы оно заработало:

awk -F'[_ ]' 'BEGIN {print "ID1 Data\tID2 Data"} NR>1{print $(NF-1)"\t\t"$NF}' file
ID1 Data        ID2 Data
12345           85485
45786           456322
12345           325489
45230           14693

Измените $NF-1 на $(NF-1) и -F'_' измените на -F'[_ ]', чтобы избавиться от пробела

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...