Объединять только определенные столбцы в CSV, если первый столбец соответствует с помощью bash - PullRequest
0 голосов
/ 14 декабря 2018

В текстовом файле содержится следующее содержимое с разделителями:

col1|col2|col3|col4|col5|col6
id1|2314|jack|nov-12|water|3294 
id2|8322|john|dec-01|sand|2334
id1|2314|jill|nov-12|oil|3294
id1|2314|jim|nov-12|ether|3294
id3|6775|mike|jan-13|dust|9348

Я хочу объединить содержимое 3-го и 5-го столбцов, в которых совпадает 1-й столбец.Выходные данные должны выглядеть следующим образом:

col1|col2|col3-1|col3-2|col3-3|col4|col5-1|col5-2|col5-3|col6
id1|2314|jack|jill|jim|nov-12|water|oil|ether|3294 
id2|8322|john|||dec-01|sand|||2334
id3|6775|mike|||jan-13|dust|||9348

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

Редактировать 1: может быть не более 5 слияний, все, что после этого должно быть добавлено в 5-й столбец с запятой, например,

col1|col2|col3-1|col3-2|col3-3|col3-4|col3-5|col4|col5-1|col5-2|col5-3|col5-4|col5-5|col6
id1|2314|jack|jill|jim|val3-4|val3-5,val3-6|nov12|water|oil|ether|val5-4|val5-5,val5-6|3294 

Редактировать 2: Как примечание стороны,Фактический файл содержит 14 столбцов, и объединение требуется для столбцов 9 и 13. Я смог адаптировать ответ @ Аллана ниже, чтобы сделать все необходимое.Кроме того, как я упоминал в комментарии к ответу @ RavinderSingh13, выходные данные автоматически обрабатываются заданием cron, поэтому число столбцов после слияния должно быть зафиксировано на 5 в каждом.

Ответы [ 4 ]

0 голосов
/ 14 декабря 2018

Вот еще одно решение с использованием Perl.Он печатает максимум 5 элементов для столбцов 3 и 5 и печатает остальные из них после столбца 6.Просто добавили "xx" и "yy" в качестве значений по умолчанию, чтобы их можно было просмотреть в выходных данных

Сценарий:

/tmp> cat csv_35col.ksh
perl -F"/\|/" -ane '
chomp($F[5]);
$id=$F[0]; 
if($.>1) { 
        if( $id ~~ @names ) 
        {
          @t3=@{ $kv3{$id} }; @t5=@{ $kv5{$id} };
          push(@t3,$F[2]); push(@t5,$F[4]);
          $kv3{$id}=[ @t3 ]; $kv5{$id}=[ @t5 ];
        }
        else
        {
        push(@names,$id); $kv{$id}=[ @F[0,1,3,5] ];
        $kv3{$id}=[ @F[2] ]; $kv5{$id}=[ @F[4] ];
        }
}
END {
$d="|"; 
for (1..6) { if($_==5 ||  $_==3) { $x=$_; for (1..5) { printf("%s|","col$x-$_")}} else { printf("%s|","col$_")}  } 
for my $x (@names) { 
@n=@{$kv{$x}}; @n3=@{$kv3{$x}}; @n5=@{$kv5{$x}};
for (0..4) { $n3[$_]= $n3[$_] ? $n3[$_] : "xx"; $n5[$_]=$n5[$_]? $n5[$_] : "yy"; }
print "\n".join($d,@n[0,1],@n3[0..4],${n[2]},@n5[0..4],${n[3]},@n3[5..$#n3],@n5[5..$#n5]);  
 }
print "\n";
} 
' $1
/tmp> 

Входные данные:

/tmp> cat jimw.csv
col1|col2|col3|col4|col5|col6
id1|2314|jack|nov-12|water|3294 
id2|8322|john|dec-01|sand|2334
id1|2314|jill|nov-12|oil|3294
id1|2314|jim|nov-12|ether|3294
id3|6775|mike|jan-13|dust|9348
/tmp> cat jimw2.csv
col1|col2|col3|col4|col5|col6
id1|2314|jack|nov-12|water|3294 
id2|8322|john|dec-01|sand|2334
id1|2314|jill|nov-12|oil|3294
id1|2314|jim|nov-12|ether|3294
id3|6775|mike|jan-13|dust|9348
id4|6776|mik1|jan-14|dast|9344
id4|6776|mik2|jan-14|dest|9344
id4|6776|mik3|jan-14|dist|9344
id4|6776|mik4|jan-14|dost|9344
id4|6776|mik5|jan-14|dst|9344
id4|6776|mik6|jan-14|dut|9344
/tmp> 

Результаты:

/tmp> csv_35col.ksh jimw.csv 
col1|col2|col3-1|col3-2|col3-3|col3-4|col3-5|col4|col5-1|col5-2|col5-3|col5-4|col5-5|col6|
id1|2314|jack|jill|jim|xx|xx|nov-12|water|oil|ether|yy|yy|3294 
id2|8322|john|xx|xx|xx|xx|dec-01|sand|yy|yy|yy|yy|2334
id3|6775|mike|xx|xx|xx|xx|jan-13|dust|yy|yy|yy|yy|9348
/tmp> 
/tmp> csv_35col.ksh jimw2.csv
col1|col2|col3-1|col3-2|col3-3|col3-4|col3-5|col4|col5-1|col5-2|col5-3|col5-4|col5-5|col6|
id1|2314|jack|jill|jim|xx|xx|nov-12|water|oil|ether|yy|yy|3294 
id2|8322|john|xx|xx|xx|xx|dec-01|sand|yy|yy|yy|yy|2334
id3|6775|mike|xx|xx|xx|xx|jan-13|dust|yy|yy|yy|yy|9348
id4|6776|mik1|mik2|mik3|mik4|mik5|jan-14|dast|dest|dist|dost|dst|9344|mik6|dut
/tmp> 
0 голосов
/ 14 декабря 2018

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

awk -F'|' '
FNR==NR{
  b[$1]=$1 in a?b[$1] FS $3:$3
  c[$1]=$1 in a?c[$1] FS $5:$5
  num1=split(b[$1],array1,"|")
  num2=split(c[$1],array2,"|")
  a[$1]=$1
  num=num1>num2?num1:num2>prev?num2:prev
  prev=num
  next
}
FNR==1{
  $3=$5=""
  while(++count<=num){
    $3=$3 OFS "col3-"count
  }
  $5=$3
  gsub("col3","col5",$5)
  print
  count=""
  next
}
!d[$1]++ && FNR>1{
  num1=split(b[$1],array1,"|")
  num2=split(c[$1],array2,"|")
  while(num1++<=num){
    b[$1]=b[$1] OFS
  }
  while(num2++<=num){
    c[$1]=c[$1] OFS
  }
  $3=b[$1]
  $5=c[$1]
  print
}'  Input_file OFS="|"  Input_file
0 голосов
/ 14 декабря 2018

$ awk -f merge_fields.awk <(perl join.pl <(sort -t'|' -k1 data.txt))

id1|2314|jack|jill|jim|nov-12|water|oil|ether|3294 
id2|8322|john|dec-01|sand|2334
id3|6775|mike|jan-13|dust|9348

join.pl

use v5.14;

readline(<>);
my @queue = ();

while (<>) {
    chomp and my @fields = split /\|/;
    say join('|', @queue) and @queue = ()
    if (@queue and @queue[0] ne @fields[0]);
    push(@queue, @fields);
}
say join('|', @queue) if @queue;

merge_fields.awk

BEGIN { OFS=FS="|" }
NF > 6 {
    for (i = 6 + 1; i < NF; i++) {
        if ($i ~ $1) {
            $3 = $3 OFS $(i+2)
            $5 = $5 OFS $(i+4)
        }
    }
}
{ print $1,$2,$3,$4,$5,$6 }
0 голосов
/ 14 декабря 2018

Первый элемент ответа (когда размер был установлен на 3 и некрасивое решение ):

awk 'BEGIN{FS=OFS="|"; print "col1|col2|col3-1|col3-2|col3-3|col4|col5-1|col5-2|col5-3|col6"}NR>1{col2[$1]=$2;col4[$1]=$4;col6[$1]=$6;if(length(col3[$1])==0){col3[$1]=$3}else{col3[$1]=col3[$1]"|"$3}if(length(col5[$1])==0){col5[$1]=$5}else{col5[$1]=col5[$1]"|"$5}}END{n=asorti(col3,oArray);for(i=1; i<=n;i++){if(index(col3[oArray[i]],"|")==0){col3[oArray[i]]=col3[oArray[i]]"||";col5[oArray[i]]=col5[oArray[i]]"||";};print oArray[i],col2[oArray[i]],col3[oArray[i]],col4[oArray[i]],col5[oArray[i]],col6[oArray[i]]}}' csvToMerge.in 
col1|col2|col3-1|col3-2|col3-3|col4|col5-1|col5-2|col5-3|col6
id1|2314|jack|jill|jim|nov-12|water|oil|ether|3294
id2|8322|john|||dec-01|sand|||2334
id3|6775|mike|||jan-13|dust|||9348

более читабельно:

    $ cat awkprof.out
    # gawk profile, created Fri Dec 14 13:12:34 2018

    # BEGIN rule(s)

    BEGIN {
 1          FS = OFS = "|"
 1          print "col1|col2|col3-1|col3-2|col3-3|col4|col5-1|col5-2|col5-3|col6"
    }

    # Rule(s)

 6  NR > 1 { # 5
 5          col2[$1] = $2
 5          col4[$1] = $4
 5          col6[$1] = $6
 5          if (length(col3[$1]) == 0) { # 3
 3                  col3[$1] = $3
 2          } else {
 2                  col3[$1] = col3[$1] "|" $3
            }
 5          if (length(col5[$1]) == 0) { # 3
 3                  col5[$1] = $5
 2          } else {
 2                  col5[$1] = col5[$1] "|" $5
            }
    }

    # END rule(s)

    END {
 1          n = asorti(col3, oArray)
 3          for (i = 1; i <= n; i++) {
 3                  if (index(col3[oArray[i]], "|") == 0) { # 2
 2                          col3[oArray[i]] = col3[oArray[i]] "||"
 2                          col5[oArray[i]] = col5[oArray[i]] "||"
                    }
 3                  print oArray[i], col2[oArray[i]], col3[oArray[i]], col4[oArray[i]], col5[oArray[i]], col6[oArray[i]]
            }
    }

BEAUTIFULLРЕШЕНИЕ

Динамическое построение col3 и col5 путем вычисления максимального количества идентичных вхождений элементов из col1

script csvmerge.awk

#function definitions

#function used to add the "|" at the end of col3, col5 when the element does not reach MAX number of occurences
function paddingfunction(MAX,input){
        output=input;
        gsub(/[^|]/,"",output);
        l=length(output);
        tmp=""
        for(u=l; u<MAX-1;u++)
        {
                tmp=tmp OFS;
        }
        return input""tmp;
}

#function used to generate nice header
function headerAppender(inputString){
        tmp=inputString;
        for(i=1;i<=MAX;i++){
                printf tmp""i OFS
        }
}

BEGIN{
        #Generate the header line
        FS=OFS="|";
        printf "col1" OFS "col2" OFS;
        headerAppender("col3-");
        printf "col4" OFS; headerAppender("col5-");
        print "col6"
}

NR>1{
        #save all the cells and concat the cells when col1 is the same
        col2[$1]=$2;
        col4[$1]=$4;
        col6[$1]=$6;
        if(length(col3[$1])==0){
                col3[$1]=$3
        }
        else{
                col3[$1]=col3[$1] OFS $3
        }
        if(length(col5[$1])==0){
                col5[$1]=$5
        }
        else{
                col5[$1]=col5[$1] OFS $5
        }
}

END{
        #sort the array
        n=asorti(col3,oArray);
        #print the cells
        for(i=1; i<=n;i++){
                print oArray[i],col2[oArray[i]],paddingfunction(MAX,col3[oArray[i]]),col4[oArray[i]],paddingfunction(MAX,col5[oArray[i]]),col6[oArray[i]];
        }
}

input1: (6 элементов в группе)

$ cat csvToMerge.in 
col1|col2|col3|col4|col5|col6
id1|2314|jack|nov-12|water|3294 
id2|8322|john|dec-01|sand|2334
id1|2314|jill|nov-12|oil|3294
id1|2314|jim|nov-12|ether|3294
id3|6775|mike|jan-13|dust|9348
id4|6776|mik1|jan-14|dast|9344
id4|6776|mik2|jan-14|dest|9344
id4|6776|mik3|jan-14|dist|9344
id4|6776|mik4|jan-14|dost|9344
id4|6776|mik5|jan-14|dst|9344
id4|6776|mik6|jan-14|dut|9344

input2: (5 элементов в группе)

$ cat csvToMerge2.in 
col1|col2|col3|col4|col5|col6
id1|2314|jack|nov-12|water|3294 
id2|8322|john|dec-01|sand|2334
id1|2314|jill|nov-12|oil|3294
id1|2314|jim|nov-12|ether|3294
id3|6775|mike|jan-13|dust|9348
id4|6776|mik1|jan-14|dast|9344
id4|6776|mik2|jan-14|dest|9344
id4|6776|mik3|jan-14|dist|9344
id4|6776|mik4|jan-14|dost|9344
id4|6776|mik5|jan-14|dst|9344

вывод 1:

$ awk -f csvmerge.awk -v MAX=`awk -F'|' ' {tot[$1]++}END{tmp=""; for (i in tot){if(tot[i]>tmp){tmp=tot[i]}}; print tmp; } ' csvToMerge.in` csvToMerge.in 
col1|col2|col3-1|col3-2|col3-3|col3-4|col3-5|col3-6|col4|col5-1|col5-2|col5-3|col5-4|col5-5|col5-6|col6
id1|2314|jack|jill|jim||||nov-12|water|oil|ether||||3294
id2|8322|john||||||dec-01|sand||||||2334
id3|6775|mike||||||jan-13|dust||||||9348
id4|6776|mik1|mik2|mik3|mik4|mik5|mik6|jan-14|dast|dest|dist|dost|dst|dut|9344

вывод 2:

$ awk -f csvmerge.awk -v MAX=`awk -F'|' ' {tot[$1]++}END{tmp=""; for (i in tot){if(tot[i]>tmp){tmp=tot[i]}}; print tmp; } ' csvToMerge2.in` csvToMerge2.in 
col1|col2|col3-1|col3-2|col3-3|col3-4|col3-5|col4|col5-1|col5-2|col5-3|col5-4|col5-5|col6
id1|2314|jack|jill|jim|||nov-12|water|oil|ether|||3294
id2|8322|john|||||dec-01|sand|||||2334
id3|6775|mike|||||jan-13|dust|||||9348
id4|6776|mik1|mik2|mik3|mik4|mik5|jan-14|dast|dest|dist|dost|dst|9344

Примечания:

-v MAX=`awk -F'|' ' {tot[$1]++}END{tmp=""; for (i in tot){if(tot[i]>tmp){tmp=tot[i]}}; print tmp; } ' csvToMerge.in`

Сохранение в переменнойMAX максимальное количество вхождений в группу, в вашем случае макс. 5, но вы можете представить себе другие ситуации, когда вам нужно сгруппировать больше элементов.

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