我正在尝试将包含3列(ATTRIBUTE_NAME,ATTRIBUTE_VALUE,ID
)的csv文件转换为每行为(ID,Attribute1,Attribute2,Attribute3,....
)的平面表。最后提供了这些表格的样本。
Python,Perl或SQL都可以。非常感谢,非常感谢您的时间和精力!
事实上,我的问题与此post非常相似,只是在我的情况下,属性的数量非常大(~300)并且在每个ID之间不一致,因此对每个属性进行硬编码可能不是一个切实可行的解决方案。
对我来说,挑战/困难的部分是:
,
),整个字符串将用双引号("
)括起来,以使读者意识到这一点。例如,ID = 53的"JPMORGAN CHASE BANK, NA, TX"
。string_country
和string_address
,因此请在逗号后输出blank/nothing
。输入属性值表如下所示。在此示例输入和输出中,我们有3个ID,其属性数可以不同,具体取决于我们是否可以从服务器获取此类属性。
ATTRIBUTE_NAME,ATTRIBUTE_VALUE,ID
num_integer,100,53
string_country,US (United States),53
string_address,FORT WORTH,53
num_double2,546.0,53
string_acc,My BankAcc,53
string_award,SILVER,53
string_bankname,"JPMORGAN CHASE BANK, NA, TX",53
num_integer,61,17
num_double,34.32,17
num_double2,200.541,17
string_acc,Your BankAcc,17
string_award,GOLD,17
string_bankname,CHASE BANK,17
num_integer,36,23
num_double,78.0,23
string_country,CA (Canada),23
string_address,VAN COUVER,23
string_acc,Her BankAcc,23
输出表应该如下所示。 (列中属性的顺序不固定。可以按字母顺序或按顺序排序。)
ID,num_integer,num_double,string_country,string_address,num_double2,string_acc,string_award,string_bankname
53,100,,US (United States),FORT WORTH,546.0,My BankAcc,SILVER,"JPMORGAN CHASE BANK, NA, TX"
17,61,34.32,,,200.541,Your BankAcc,GOLD,CHASE BANK
23,36,78.0,CA (Canada),VAN COUVER,,Her BankAcc,,
答案 0 :(得分:1)
这个程序可以按照你的要求进行。它期望输入文件的名称作为命令行上的参数。
更新仔细查看数据,我发现并非所有数据字段都可用于每个ID。如果字段的保存顺序与文件中出现的顺序相同,那么事情会变得更加复杂。
此程序通过扫描文件并将所有数据累积到散列%data
来工作。同时,它构建一个哈希%headers
,保持每个标题的位置出现在每个ID值的数据中。
扫描文件后,通过查找包含两个标头信息的每对的第一个ID对收集的标头进行排序。整个集合中该对的排序顺序必须与它们在该ID的数据中出现的顺序相同,因此只需使用<=>
比较两个位置值即可。
一旦创建了一组已排序的标头,就会转储%data
哈希,使用哈希切片访问每个ID的完整值列表。
更新2 现在,我意识到数据的庞大规模,我可以看到我的第二次尝试也存在缺陷,因为它试图将所有信息读入输出前的内存。除非你有一个大约1TB内存的怪物机器,否则这不会起作用!
您可能会从此版本中获得一些里程数。它在文件中扫描两次,第一次读取数据,以便可以创建和排序完整的标题名称集,然后再次读取每个ID的数据并输出。
让我知道它是否不适合你,因为我仍然可以采取一些措施来提高内存效率。
use strict;
use warnings;
use 5.010;
use Text::CSV;
use Fcntl 'SEEK_SET';
my $csv = Text::CSV->new;
open my $fh, '<', $ARGV[0] or die qq{Unable to open "$ARGV[0]" for input: $!};
my %headers = ();
my $last_id;
my $header_num;
my $num_ids;
while (my $row = $csv->getline($fh)) {
next if $. == 1;
my ($key, $val, $id) = @$row;
unless (defined $last_id and $id eq $last_id) {
++$num_ids;
$header_num = 0;
$last_id = $id;
print STDERR "Processing ID $id\n";
}
$headers{$key}[$num_ids-1] = ++$header_num;
}
sub by_position {
for my $id (0 .. $num_ids-1) {
my ($posa, $posb) = map $headers{$_}[$id], our $a, our $b;
return $posa <=> $posb if $posa and $posb;
}
0;
}
my @headers = sort by_position keys %headers;
%headers = ();
print STDERR "List of headers complete\n";
seek $fh, 0, SEEK_SET;
$. = 0;
$csv->combine('ID', @headers);
print $csv->string, "\n";
my %data = ();
$last_id = undef;
while () {
my $row = $csv->getline($fh);
next if $. == 1;
if (not defined $row or defined $last_id and $last_id ne $row->[2]) {
$csv->combine($last_id, @data{@headers});
print $csv->string, "\n";
%data = ();
}
last unless defined $row;
my ($key, $val, $id) = @$row;
$data{$key} = $val;
$last_id = $id;
}
<强>输出强>
ID,num_integer,num_double,string_country,string_address,num_double2,string_acc,string_award,string_bankname
53,100,,"US (United States)","FORT WORTH",546.0,"My BankAcc",SILVER,"JPMORGAN CHASE BANK, NA, TX"
17,61,34.32,,,200.541,"Your BankAcc",GOLD,"CHASE BANK"
23,36,78.0,"CA (Canada)","VAN COUVER",,"Her BankAcc",,
答案 1 :(得分:0)
使用Text::CSV
from CPAN:
#!/usr/bin/env perl
use strict;
use warnings;
# --------------------------------------
use charnames qw( :full :short );
use English qw( -no_match_vars ); # Avoids regex performance penalty
use Text::CSV;
my $col_csv = Text::CSV->new();
my $id_attr_csv = Text::CSV->new({ eol=>"\n", });
$col_csv->column_names( $col_csv->getline( *DATA ));
while( my $row = $col_csv->getline_hr( *DATA )){
# do all the keys but skip if ID
for my $attribute ( keys %$row ){
next if $attribute eq 'ID';
$id_attr_csv->print( *STDOUT, [ $attribute, $row->{$attribute}, $row->{ID}, ]);
}
}
__DATA__
ID,num_integer,num_double,string_country,string_address,num_double2,string_acc,string_award,string_bankname
53,100,,US (United States),FORT WORTH,546.0,My BankAcc,SILVER,"JPMORGAN CHASE BANK, NA, TX"
17,61,34.32,,,200.541,Your BankAcc,GOLD,CHASE BANK
23,36,78.0,CA (Canada),VAN COUVER,,Her BankAcc,,