質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
CSV

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

Perl

Perlは多目的に使用される実用性が高い動的プログラミング言語のひとつです。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

Q&A

解決済

2回答

717閲覧

Perlでcsvファイルのデータ処理について教えてください

soso

総合スコア12

CSV

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

Perl

Perlは多目的に使用される実用性が高い動的プログラミング言語のひとつです。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

0グッド

0クリップ

投稿2018/07/19 08:10

前提・実現したいこと

perlでlinuxサーバー内のcsvのログ解析をしており、起点($original)と
目的地($destination)の組合せ別の集計値を集計値の降順で表示したいです。

ここに質問の内容を詳しく書いてください。
perlでcsvファイルを集計するプログラムを作成していますが、行き詰っておりお知恵を頂きたく投稿しました。
csvファイル内の起点($original)と目的地($destination)という項目があり、その二つの組合せの合計数をそれぞれ集計し、集計値が降順に並ぶようなプログラムを作成したいと考えています。

現状では下記のように起点($original)ごとの組合せでの集計値が降順に並んでいますが、
|original|destination|集計値|
|100|101|10|
|100|102|5|
|100|103|1|
|90|101|15|
|90|102|4|
|80|101|8|
|80|102|7|

下記のように全ての集計値が降順で並ぶようにプログラムをかきたいと思っております。

|original|destination|集計値|
|90|101|15|
|100|101|10|
|80|101|8|
|80|102|7|
|100|102|5|
|90|102|4|
|100|103|1|

何卒お力添えのほどお願いいたします。

■■な機能を実装中に以下のエラーメッセージが発生しました。

発生している問題・エラーメッセージ

該当のソースコード

ここに言語名を入力
Perl
use strict;
use warnings;

open(FILE, 'example.csv') or die "$!";

my $info = {};
while (my $line = <FILE>) {
my($DateTime, $original, $destination, $stopover1, $stopover2, $stopover3, $Model, $tiles, $Method, $Time,
$TimeDifference, $Flag, $Date, $searchTime, $searchDay, $rideDate, $rideTime, $rideDay)
= split(/,/, $line);

++$info->{$original}->{$destination};

}
close(FILE);

foreach my $original( sort keys %$info){

foreach my $destination(sort {${$info->{$original}}{$b} <=> ${$info->{$original}}{$a}} keys %{$info->{$original}}){ my $count = $info->{$original}{$destination}; my @rec = ($original, $destination, $count); print join(',', @rec). "\n"; }

}

試したこと

ここに問題に対して試したことを記載してください。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

まず、実際のデータが全角数字なら、半角数字に修正してください。
これは適当なエディタかなにかで実行出来るかと思います。
後述のスクリプト内に埋め込む事も出来ますが、割愛します。
あと、質問のコードに書かれている $DateTimeなどは例に出されたセルには
存在していないため、回答コードでは考慮していません。

で、半角数字に直した data.txt を相手にするとして。

|100|101|10| |100|102|5| |100|103|1| |90|101|15| |90|102|4| |80|101|8| |80|102|7|

要望は、「|区切りの、3最後のセルで数字の降順ソートしたい。」に簡略化されますので、
以下の script.pl で実現できます。

#!/usr/bin/env perl my @d ; open my $fh, '<', 'data.txt' ; while(<$fh>){ push @d, [ $_, split /\|/, $_ ] ; } close $fh ; print $_->[0] for sort { $b->[4] <=> $a->[4] } @d;
$ perl script.pl |90|101|15| |100|101|10| |80|101|8| |80|102|7| |100|102|5| |90|102|4| |100|103|1|

やってることは、

  1. それを元データと、セルに分割した文字列を配列に入れる push @d, [ $_, ... ] ;
  2. 3セル目で降順ソート(二次元配列上では5カラム目) sort { $b->[4] <=> $a->[4] }
  3. 元データをプリント print $_->[0]

です。

必要でないなら、ハッシュなどを使わずに、シンプルに実行するのはいかがでしょうか。
もし、記載外で他のセルが処理に必要なら、最終行の for 文を丁寧に書けば良いかと思います。

for my $r ( sort { $b->[4] <=> $a->[4] } @d ){ my $ori = $r->[2] ; my $des = $r->[3] ; my $tot = $r->[4] ; ... }

投稿2018/07/19 13:56

bunzaemon

総合スコア118

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

soso

2018/07/20 09:34

ありがとうございました。 配列でやることをご指摘いただき、何とか思ったような処理に辿り着きました。
guest

0

ベストアンサー

perlのsort関数では独自の「比較関数」が利用できます。

perl

1@a = (1,3,2,4,10,5,20); 2 3# デフォルトの比較関数は{$a cmp $b} 4@a1 = sort @a; 5# 数値として比較して昇順にソート 6@a2 = sort {$a<=>$b} @a; 7# 数値として比較して降順にソート 8@a3 = sort {$b<=>$a} @a; 9

比較関数の書き方は独特なのでマニュアルを確認してください。要点は、
0. 比較すべき要素二つをそれぞれ$a, $bで表す。
0. $a>$bなら1, $a<$bなら-1, $a==$bなら0を返すように式を立てる。

次に対象データですが、2段のハッシュになっているため、少しややこしいです。ソートをかけるにはまずこの階層を解きたいところです。map関数が使えるでしょう。

これらを組み合わせ、無名リスト化→ソート→出力という順番を考えてみました。試してませんが、最後のforeachループを次のような雰囲気で書き換えればいいでしょう。

map {printf "%s %s %s\n", $_->[0], $_->[1], $_->[2]} sort {$b->[2] <=> $a->[2]} map {$k1=$_; map {$k2=$_; [$k1, $k2, $x->{$k1}{$k2}] } keys $x->{$k1}} keys %$info

投稿2018/07/19 10:52

KojiDoi

総合スコア13671

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KojiDoi

2018/07/20 02:57

できるだけオリジナルのソースを活かす方法として考えてみたものですが、本当は他の方が答えているようにハッシュではなく配列で処理するのが遥かに素直です。
soso

2018/07/20 09:28

ご丁寧に教えていただきましてありがとうございました。 教えていただいたやり方を理解できず、コメントで頂いたように配列にして並び替えることができました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問