Posted at

CSVファイルの列データを結合するperl script

More than 1 year has passed since last update.

実験データのCSVファイルの列を連結する作業をお願いされたので、perl scriptをつくりましたので、おすそ分けします。


CSVファイルの形式

ファイル名 foo_range5to9_test.csv

5
6
7
8
9

1
.
.
.
.
.

2
.
.
.
.
.

3
.
.
.
.
.

...
.
.
.
.
.

ファイル名 foo_range10to14_test.csv

10
11
12
13
14

1
*
*
*
*
*

2
*
*
*
*
*

3
*
*
*
*
*

...
*
*
*
*
*

これが、フォルダー(SRC)に入っている

foo_range5to9_test.csv

foo_range10to14_test.csv

bar_range0to4_test.csv

bar_range5to9_test.csv


結合結果

outフォルダーにまとめたファイルができる

foo_range5to14_test.csv

bar_range0to9_test.csv

5
6
7
8
9
10
11
12
13
14

1
.
.
.
.
.
*
*
*
*
*

2
.
.
.
.
.
*
*
*
*
*

3
.
.
.
.
.
*
*
*
*
*

...
.
.
.
.
.
*
*
*
*
*


スクリプト

% perl スクリプト.pl SRC/*


CSVファイルの列結合

# CSVファイルの列結合perl script

#
# copyright 2018 0x20FE

#結合したいファイル名を引数として呼ぶ
# 例 perl merge.pl SRC/foo_range10to14_test.csv SRC/foo_range5to9_test.csv SRC/bar_range0to4_test.csv SRC/bar_range5to9_test.csv
# このときは out/foo_range0to9_test.csvとout/var_range0to9_test.csv を出力する
# ファイル名は、(ディレクトリ名)/(プレフィックス)_範囲識別名(数字)to(数字)_(ポストフィックス).csv

$PARKEY='range';
@IN=@ARGV;

& fn2grp(@IN);
#----------------------------------
sub fn2grp{
my @files=@_;
%FN=();# ファイル名
foreach $fn (@files){
($d,$hd,$st,$en,$tl)=( $fn =~ m@^([^/]*)/(.*)_$PARKEY(¥d*)to(¥d*)_(.*).csv$@) or next;
# print "$hd-$tl-$st-$en¥n";
# まとめるグループ名を作成する
$grp="$hd,$tl";
$FN{$grp}=$FN{$grp}.",$fn"; # %FNにグループ名をキーとしてファイル名をカンマでつなげた文字データを作成する
# 範囲の最小と最大を記録する
$ST{$grp}=exists($ST{$grp})?($ST{$grp}<$st?$ST{$grp}:$st):$st;
$EN{$grp}=$EN{$grp}>$en?$EN{$grp}:$en;
# 各ファイルの開始列データを保持する
$SVal{$grp}=$SVal{$grp}.",".$st;
}

############################################
# (i) %FN 列結合するCSVファイル名を持つ連想配列
# (o) 列結合したCSVデータ・ファイル
############################################
foreach $grp (keys %FN){
# print "$grp : $ST{$grp}-$EN{$grp}¥n";
# print "$grp : $FN{$grp}¥n";

#--- gen group name ---
# グループ名を作成する  元のファイル名に書かれていた、範囲数の最小と最大を使用
$st= $ST{$grp};$en=$EN{$grp};
($hd,$tl)=split(/,/,$grp);
$gnam=$hd."_$PARKEY".$st."to".$en."_".$tl;
print "### $gnam ###¥n";
# print "+++ $FN{$grp}¥n";
# print "*** $SVal{$grp}¥n";

#--- get filenames ----
#ファイル名、開始番号のリストを取り出す
@fns=split(/,/,$FN{$grp});
shift(@fns);# drop dummy
@svs=split(/,/,$SVal{$grp});
shift(@svs);# drop dummy

#========================================================
#リストに記載された順が数字の大小と一致しないことがあるのでソートする
#========================================================
# SRC/foo_range10to14_test.csv SRC/foo_range5to9_test.csv なら
# まず連想配列の開始番号とファイル名を対応づける
# $fnmap{10} => SRC/foo_range10to14_test.csv
# $fnmap{5} => SRC/foo_range5to9_test.csv
undef %fnmap;
for($i=0;$i<=$#svs;$i++){
$fnmap{$svs[$i]}=$fns[$i];
}
#次に開始番号をソートして、キーを作成する
# 5 , 10
# これをキーととしてファイル名を取り出すことで、ソートが完了する
@fns_sort=();
for $sv (sort {$a <=> $b} keys %fnmap){
print $sv ," - ", $fnmap{$sv} ,"¥n";
push(@fns_sort,$fnmap{$sv});
}
# next;
@fns = @fns_sort;
#========================================================

########################################
# 結合処理
# ファイル名のリストを受け取り、2列目以降の列データを先頭ファイルのデータに追加する
#
# (i) @fns ファイル名のリスト
# (o) @TOP
########################################
# 最初のファイルを読み込み、改行コードを削除する
$top=shift(@fns);
undef @TOP;
open(FIN,$top);@TOP=<FIN>; close(FIN);
chop(@TOP);
chop(@TOP);
########################################
# のこりのファイルを読み込み、列に追加する
foreach $fn (@fns){
# ファイルを読み込み、改行コードを削除する
print "----$fn---¥n";
undef @IN;
open(FIN,$fn);@IN=<FIN>; close(FIN);
chop(@IN);
chop(@IN);
# 読み込んだファイルの行数が、最初のファイルの行数と一致していることを確認する
#print "$#TOP,$#IN¥n";
($#TOP == $#IN) or die "FILE LENGTH inconstent! :$fn";
#読み込んだファイルの各行に対して、先頭列のデータを捨て、先頭ファイルのデータに列データを追加する
for($i=0;$i<=$#TOP;$i++){
@in = split(/,/,$IN[$i]);shift(@in); # drop 1st column
$in = join(',',@in);
$TOP[$i] .= " , " . $in;
#print $TOP[$i]," , " . $IN[$i],"¥n";
#print $TOP[$i],"¥n";
#last if $i==2;
}
}
## 末尾に改行記号を追加して出力する cr/lf
open(FOUT,">out/$gnam".".csv") or die;
print FOUT "必要に応じてヘッダを追加するならここにいれる¥r¥n";
for($i=0;$i<=$#TOP;$i++){
print FOUT $TOP[$i] ,"¥r¥n";
}
close(FOUT);
}
}