用于处理CSV文件的Perl脚本,聚合分布在多个记录上的属性

时间:2012-09-16 21:32:52

标签: perl csv

对于模糊的问题感到抱歉,我正在努力思考如何更好地说出来!

我有一个看起来有点像这样的CSV文件,只有更大的文件:

550672,1
656372,1
766153,1
550672,2
656372,2
868194,2
766151,2
550672,3
868179,3
868194,3
550672,4
766153,4

第一列中的值是ID号,第二列可以描述为属性(因为缺少更好的词......)。 ID号550672具有属性1,2,3,4。任何人都可以指出我如何开始解决如何生成所有ID号码的字符串?我理想的输出是一个新的csv文件,看起来像:

550672,1;2;3;4
656372,1;2
766153,1;4

我非常喜欢Perl宝宝(只有3天大!)所以我真的很欣赏方向,而不是直接的解决方案,我决心学习这些东西,即使它花了我余下的时间!我尽我所能尽力调查它,虽然我认为我已经因为不知道该寻找什么而陷入困境。我能够读入并解析CSV文件(我甚至可以删除重复的值!)但是这真的是我为它丢弃的地方。任何帮助将不胜感激!

5 个答案:

答案 0 :(得分:4)

我认为最好是为您提供工作计划而不是一些提示。提示只能带你到目前为止,如果你花时间了解这段代码,它将为你提供良好的学习体验

最好在处理CSV数据时使用Text::CSV,因为已经为您完成了所有调试

use strict;
use warnings;

use Text::CSV;

my $csv = Text::CSV->new;

open my $fh, '<', 'data.txt' or die $!;
my %data;
while (my $line = <$fh>) {
  $csv->parse($line) or die "Invalid data line";
  my ($key, $val) = $csv->fields;
  push @{ $data{$key} }, $val
}

for my $id (sort keys %data) {
  printf "%s,%s\n", $id, join ';', @{ $data{$id} };
}

<强>输出

550672,1;2;3;4
656372,1;2
766151,2
766153,1;4
868179,3
868194,2;3

答案 1 :(得分:3)

首先是寻求方法而不是解决方案的道具。 正如您可能已经在perl中找到的那样,有多种方法可以做到。

我将采取的方法是;

use strict;  # will save you big time in the long run

my %ids      # Use a hash table with the id as the key to accumulate the properties
open a file handle on csv or die
while (read another line from the file handle){
  split line into ID and property variable  # google the split function
  append new property to existing properties for this id in the hash table  # If it doesn't exist already, it will be created
}

foreach my $key (keys %ids) {
  deduplicate properties
  print/display/do whatever you need to do with the result
}

这种方法意味着您需要迭代整个集合两次(一次在内存中),因此根据可能存在问题的数据集的大小。 更复杂的方法是使用哈希表的哈希表在初始步骤中执行de duplication,但是根据您希望/需要多快地运行它,这在第一个实例中可能不值得。

退房 this question 有关如何进行重复数据删除的讨论。

答案 2 :(得分:2)

好吧,在perl中将文件作为stdin打开,假设每行有两列,然后使用左列作为散列标识符迭代所有行,并将右列收集到由散列键指向的数组中。在输入文件的末尾,您将获得数组的哈希值,因此迭代它,打印哈希键并分配以“;”分隔的数组元素或者你想要的任何其他标志。

然后你去

dtpwmbp:~ pwadas$ cat input.txt 
550672,1
656372,1
766153,1
550672,2
656372,2
868194,2
766151,2
550672,3
868179,3
868194,3
550672,4
766153,4
dtpwmbp:~ pwadas$ cat bb2.pl 
#!/opt/local/bin/perl

my %hash;
while (<>)
{
    chomp;
    my($key, $value) = split /,/;
    push @{$hash{$key}} , $value ;
}

foreach my $key (sort keys %hash)
{
     print $key . "," . join(";", @{$hash{$key}} ) . "\n" ;
}
dtpwmbp:~ pwadas$ cat input.txt | perl -f bb2.pl 
550672,1;2;3;4
656372,1;2
766151,2
766153,1;4
868179,3
868194,2;3
dtpwmbp:~ pwadas$

答案 3 :(得分:2)

perl -F"," -ane 'chomp($F[1]);$X{$F[0]}=$X{$F[0]}.";".$F[1];if(eof){for(keys %X){$X{$_}=~s/;//;print $_.",".$X{$_}."\n"}}'

答案 4 :(得分:1)

另一种(不是perl)方式,它更短,更优雅:

#!/opt/local/bin/gawk -f

BEGIN {FS=OFS=",";}

NF > 0 { IDs[$1]=IDs[$1] ";" $2; }

END { for (i in IDs) print i, substr(IDs[i], 2); }

第一行(在指定解释器之后)将输入FIELD SEPARATOR和OUTPUT FIELD SEPARATOR设置为逗号。第二行检查我们有超过零个字段,如果你这样做,则ID($ 1)数字为密钥,$ 2为数值。你为所有行都这样做。

END语句将以未指定的顺序打印出这些对。如果要对它们进行排序,则必须选择asorti gnu awk函数或将此代码段的输出与管道连接到sort -t, -k1n,1n