두 개의 csv 파일이 있습니다.
파일 1에는 헤더가 포함되어 있습니다. 파일 2에는 데이터가 포함되어 있습니다.
파일 1 형식: file1.csv
id,abc,xyz,aaa,bbb,ccc
파일 2 형식: file2.csv
id,source,value
1,abc,100
1,xyz,200
2,aaa,300
2,bbb,400
2,ccc,500
이제 file2.csv의 소스 열에 있는 데이터를 file1.csv의 헤더와 일치시켜야 하며 출력은 다음과 같아야 합니다.
id,abc,xyz,aaa,bbb,ccc
1,100,200,null,null,null
2,null,null,300,400,500
답변1
이는 일반적으로 녹지 않거나 어수선한 작업입니다.
밀러 사용(http://johnkerl.org/miller/doc) 그리고
id,source,value 1,abc,100 1,xyz,200 2,aaa,300 2,bbb,400 2,ccc,500
넌 달릴 수 있어
mlr --csv reshape -s source,value then unsparsify input.csv
그리고 가지고
id,abc,xyz,aaa,bbb,ccc 1,100,200,,, 2,,,300,400,500
답변2
저항할 수 없어... 회선 소음...
perl -F, -slE'if(@ARGV){say;@h=@F}elsif($.>1){$d{$F[0]}->@{@h}=($F[0],("null")x@h)unless$d{$F[0]};$d{$F[0]}{$F[1]}=$F[2]}close ARGV if eof}END{say$d{$_}->@{@h}for keys%d' -- -,=, file{1,2}.csv
아니면 (일종의) 현명한 농담
perl -F, -lane '
if (@ARGV) {print; @sources = @F[1..$#F]} # the first file
elsif ($. > 1) { # the 2nd file, minus the header
$data{$F[0]}->@{@sources} = ("null") x @sources unless $data{$F[0]};
$data{$F[0]}{$F[1]} = $F[2];
}
close ARGV if eof; # reset $. for each new file
} END {
$, = ",";
print $_, $data{$_}->@{@sources} for sort keys %data
' file1.csv file2.csv
또는 "combine.pl"입니다.
#!/usr/bin/env perl
use v5.22;
use Path::Tiny; # convenience module from CPAN
# read the header from the first file
my $file = path("file1.csv");
my @lines = $file->lines;
my $header = $lines[0];
chomp $header;
my @sources = split /,/, $header;
# read the data from the second file
$file = path("file2.csv");
chomp( @lines = $file->lines );
shift @lines; # ignore the header
my %data;
for my $line (@lines) {
my ($id, $source, $value) = split /,/, $line, 3;
if (not exists $data{$id}) {
# initialize the output data for a new id
$data{$id}->@{ @sources } = ($id, ("null") x scalar(@sources));
}
# and store this value
$data{$id}{$source} = $value;
}
# output the results
say $header;
$, = ",";
for my $id (sort {$a <=> $b} keys %data) {
say $data{$id}->@{@sources};
}
그 다음에:perl combine.pl > output.csv
답변3
...그리고 피할 수 없는 awk
조언:
awk -F, '
function PRT() {printf "%d", ID # print first field: ID
for (i=2; i<=MAX; i++) printf ",%s",PF[i]?PF[i]:"null" # print popuated fields in sequence, "null" if empty
printf ORS # line feed
}
NR==FNR {print # in first file; print header
for (MAX=n=split ($0, T); n; n--) COL[T[n]] = n # split header and find column No. per header field
next # no further processing of this line
}
FNR > 2 && # skip printout for first ID (in second line of file2)
$1 != ID {PRT() # print if new ID found
split ("", PF) # empty the print array
}
{ID = $1 # retain ID
PF[COL[$2]] = $3 # collect col values into respective column
}
END {PRT() # print last IDs record
}
' file[12] # shells "pattern matching" expands resp. files
id,abc,xyz,aaa,bbb,ccc
1,100,200,null,null,null
2,null,null,300,400,500