我已经构建了几个脚本来组织测试设备的数据输出,但我遇到了这个问题。
测试设备监视来自多个主体的四种类型的输入(Data1,Data2,Data3,Data4)(具有标识符ID1,ID2等),并且每个输入以间隔记录日期和时间戳。设备转储的CSV文件的组织方式如下:
Start,Date,Time0
Subject,ID1,ID2,[...],ID#
Date,Time1
Data1,aa1,aa2,[...],aa#
Data2,ba1,ba2,[...],ba#
Data3,ca1,ca2,[...],ca#
Data4,da1,da2,[...],da#
Date,Time2
Data1,ab1,ab2,[...],ab#
Data2,bb1,bb2,[...],bb#
Data3,cb1,cb2,[...],cb#
Data4,db1,db2,[...],db#
......等等。
“开始”将此行标识为数据的开头; “主题”将该行标识为包含主题ID的行; “Data1” - “Data4”将该行标识为包含由前一个日期和时间指示的特定时间间隔内该数据类型的数据的行。
输出数据因此被分成多个块,这对于设备制造商来说确实是一个不幸的选择,特别是因为数据是在几天或几周内每隔几分钟收集一次。要分析数据而不必手动选择每个第6行,我们需要将所有数据类型分组为块,如下所示:
Data1,Subject,ID1,ID2,[...],ID#
Date,Time1,aa1,aa2,[...],aa#
Date,Time2,ab1,ab2,[...],ab#
...
Data2,Subject,ID1,ID2,[...],ID#
Date,Time1,ba1,ba2,[...],ba#
Date,Time2,bb1,bb2,[...],bb#
...
目标是将四种数据类型中的每一种都放在不同的块中,以便任何给定主题(ID1到ID#)的时间段数据将在一个列中,日期和时间作为初始列。 (上面的“DataX”和“Subject”仅用作列标题。)
目前我这样做是将每一行放入一个单独的数组中。这是完成任务的快捷方式;脚本抓取时间和日期,并将ID行推入四个数组中的每一个(每种数据类型一个),然后根据数据类型依次添加每个数据行。输出只是逐行打印每个数组,添加一个空行,然后打印下一个数组。这是有效的,但理想情况下,我想按主题ID对数据列进行排序,然后打印出数据,而不会丢失按日期和时间戳排列的垂直排序。 (因为数据已经垂直排序,我在打印之前目前在阵列上没有排序功能。)
最简单的方法是什么?在心理上,我无法解析如何将行Y,列X中的数据与CSV文件中的列X中的主题ID相关联。我使用的每个其他数据输出文件都将主题ID保存为每行中的第一项,或者每个主题有一个文件,这样可以更容易。
注意:由于时间/日期是各自的,我每个都使用一个变量;如果脚本检测到包含新时间和/或日期的行,则会更新变量值。
编辑 - 我纳入了一些Borodin的建议(让FH按行而不是段落处理)。我将主题行中的数据拉入数组(@ids),并使用日期/时间和ID作为键将数据行推送到哈希:
my ($datatype, @fields) = @line;
push @keys, $datatype unless exists $data{$datatype};
my $datetime = "$date\,$time";
push @timestamps, $datetime unless exists $data{$datetime};
for my $i ( 0 .. $#fields) {
push @{$data{$datetime}{$ids[$i]}}=>$fields[$i]
};
我也将日期 - 时间对放入第二个数组以维持顺序(@timestamps)。 此时的问题是我在将值打印出来时遇到问题。目前正在尝试:
foreach my $date (keys %data) {
print OUT $date;
foreach my $id (@ids) {
foreach my $s (keys %{$data{$date}}) {
if ( exists($data{$date}{$id}) ) {
print OUT ",", $data{$date}{$id}
}
else {
print OUT ",";
}
}
}
print OUT "\n"; # close printing on a given date
}
继续获取垃圾输出(打印哈希引用,而不是实际值!)。 Dumper输出如下所示:
$VAR1 = {
'date,time' => [
'ID1' => [
'0.00'
]
'ID2' => [
'0.12',
]
'ID3' => [
'0.17',
]
'ID4' => [
'0.22',
]
]
}
};
,打印输出如下:
date,time,ARRAY(0x7f91c1030f60),ARRAY(0x7f91c1030f60),ARRAY(0x7f91c1030f60),ARRAY(0x7f91c1030f60)
对不起,到目前为止,这些例子引起了解释上的问题。输入文件中有大量过多的数据和文本,我只包含了我试图提取和排序的部分的高度简化版本。
答案 0 :(得分:1)
该程序将所有数据读入哈希值,并以您需要的转换状态重现它。这很好,除非数据很大并且不容易放入你可用的内存中,在这种情况下你需要一个不同的解决方案。
程序需要输入文件的名称作为命令行参数,如果没有提供,则默认为data.csv
。它将$/
设置为空字符串以启用Perl"段落模式"输入,其中数据被读取到下一个空白行或文件末尾。这意味着一次读取给定时间间隔的所有数据,并且必须在处理之前将其进一步拆分为单独的行。
use strict;
use warnings;
my ($subject, @ids);
my @sort_order;
my (%data, @keys);
my ($file) = @ARGV;
$file //= 'data.csv';
open my $fh, '<', $file or die qq{Unable to open file "$file" for reading: $!};
local $/ = '';
while (<$fh>) {
my @rows = split /\n/;
unless ($subject) {
($subject, @ids) = split /,/, $rows[1];
@sort_order = sort { $ids[$a] cmp $ids[$b] } 0 .. $#ids;
next;
}
my ($date, $time) = split /,/, shift @rows;
for (@rows) {
my ($id, @fields) = split /,/;
push @keys, $id unless exists $data{$id};
push @{ $data{$id} }, [$date, $time, @fields[@sort_order]];
}
}
for my $key (@keys) {
print join(',', $key, $subject, @ids[@sort_order]), "\n";
print join(',', @$_), "\n" for @{ $data{$key} };
print "\n";
}
<强>输出强>
Data1,Subject,ID#,ID1,ID2,[...]
Date,Time1,aa#,aa1,aa2,[...]
Date,Time2,ab#,ab1,ab2,[...]
Data2,Subject,ID#,ID1,ID2,[...]
Date,Time1,ba#,ba1,ba2,[...]
Date,Time2,bb#,bb1,bb2,[...]
Data3,Subject,ID#,ID1,ID2,[...]
Date,Time1,ca#,ca1,ca2,[...]
Date,Time2,cb#,cb1,cb2,[...]
Data4,Subject,ID#,ID1,ID2,[...]
Date,Time1,da#,da1,da2,[...]
Date,Time2,db#,db1,db2,[...]
答案 1 :(得分:0)
您是否考虑过将数据放入SQL数据库? [例如。 &#34;服务器少&#34;的SQLite]
这可能是一种矫枉过正,但它应该为大数据集提供更好的灵活性。