对齐从perl哈希创建的数据表

时间:2011-10-03 19:20:11

标签: perl hashtable alignment

我正在尝试编写一个脚本来处理行为测试设备的输出。我需要在生成的CSV文件中按时间戳对齐所有数据。这是一个问题:测试运行的开始时间不同(它接近但不准确 - 可以关闭几秒到几分钟)。我可以得到我想要的输出,我想我对如何对齐所有变量有一个好主意,但不知道如何实现它。

所有数据都包含两个级别的哈希值(%hash {id} {vars}),所有变量都存储为数字以保持简单(变量名称从打印输出的数组中读取)。从输入文件中删除所有数据后,脚本将遍历散列并打印出数据,如下所示:

Variable 1
ID #1   data1   data2   data3...
ID #2   data1   data2   data3...
...
Variable 2
...

等等。

这些是24小时录音。所有主题的最后一个数据点(var = 20)都很轻:数据在白天和夜晚都会显示为“ON”或“OFF”。我能看到的最佳对齐方法是使用灯光OFF标记来对齐数据。

我的想法如下:
1.找到每个ID的第一个位置,其中var'20'='OFF'并记录位置
2.找出哪个ID具有OFF的最大位置(即最早开始记录的ID)
3.将空值对添加到每个其他主题,直到所有的OFF位置相同。

例如,如果数据每分钟记录一次,并且一个主体的OFF时间比所有其他主体晚5分钟,则将5个空数据点添加到所有其他主题以对齐数据。

必须为每个主题的所有数据点进行此操作,而不仅仅是灯开/关测量。

这种方法有用吗?如果是这样,我怎么能实现这个呢?

**请注意,我需要能够将其打包为在多台计算机上运行的独立脚本,因此我不能指望默认情况下未安装的perl模块。

- 按请求编辑:示例。输入数据看起来像这样(它是一个CSV文件)

ID,     TIME,      DATA1,  DATA2,  DATA3, [...] ,  LIGHT  
Subj1,  10:00:00,  data1,  data2,  data3, [...] ,  ON  
Subj1,  10:00:30,  data1,  data2,  data3, [...] ,  ON  
Subj1,  10:01:00,  data1,  data2,  data3, [...] ,  OFF  
Subj1,  10:01:00,  data1,  data2,  data3, [...] ,  OFF  

对于另一个主题,数据可能如下所示:

ID,     TIME,      DATA1,  DATA2,  DATA3, [...] ,  LIGHT  
Subj2,  09:59:27,  data1,  data2,  data3, [...] ,  ON  
Subj2,  09:59:57,  data1,  data2,  data3, [...] ,  ON  
Subj2,  10:00:27,  data1,  data2,  data3, [...] ,  ON  
Subj2,  10:00:57,  data1,  data2,  data3, [...] ,  OFF  
Subj2,  10:01:27,  data1,  data2,  data3, [...] ,  OFF  

脚本从所有文件中获取每一行,并将它们添加到由ID键控的哈希值,每个数据列的一个级别由列号键入。对于这两个文件,hash看起来像这样:

$VAR1 = {
         'Subj1' => {
                     '1' => [
                             data1
                             data1
                             ...
                             ]
                      '2' => [
                             data2
                             data2
                             ...
                             ]
                     ...
                     '20' => [
                             ON
                             ON
                             ...
                    }
         'Subj1' => {
                     '1' => [
                             data1
                             data1
                             ...
                             ]
                      '2' => [
                             data2
                             data2
                             ...
                             ]
                     ...
                     '20' => [
                             ON
                             ON
                             ...
                    }
        };

使用foreach循环输出数据:

foreach my $k (sort {$a cmp $b} keys %data) { 
    print OUT $k, "\,";
    foreach my $d ( @{ $data{$k}{$i} } ) { print OUT $d, "\,"; }
    print OUT "\n";
    }

输出如下:

TIME  
Subj1,  10:00:00,  10:00:30,  10:01:00,  10:01:30,
Subj2,  09:59:27,  09:59:57,  10:00:27,  10:00:57,  10:01:27,
DATA1
Subj1,  data1,  data1,  data1,  data1,  data1,  
Subj2,  data2,  data2,  data2,  data2,  data2,  data2,
[ ... all other data ... ]
LIGHT
Subj1,  ON,  ON,  OFF, OFF,
Subj2,  ON,  ON,  ON,  OFF, OFF,

我需要做的是通过LIGHT中的ON / OFF列对齐所有数据,方法是添加如下的空值:

TIME  
Subj1,          ,  10:00:00,  10:00:30,  10:01:00,  10:01:30,
Subj2,  09:59:27,  09:59:57,  10:00:27,  10:00:57,  10:01:27,
DATA1
Subj1,       ,  data1,  data1,  data1,  data1,  data1,  
Subj2,  data2,  data2,  data2,  data2,  data2,  data2,
[ ... all other data ... ]
LIGHT
Subj1,    ,  ON,  ON,  OFF, OFF,
Subj2,  ON,  ON,  ON,  OFF, OFF,

试图找出最好的方法。对不起,这很长......

3 个答案:

答案 0 :(得分:1)

这对你有什么用?

它确实使用了List::Util::max,但这已经标准化了一段时间,如果你没有它,很容易自己编写。

use List::Util qw(max);
use strict;
use warnings;

my $ALLDATA = {
         'Subj1' => {
                     '1' => [
                             'data1',
                             'data1',
                             ],
                      '2' => [
                             'data2',
                             'data2',
                             ],
                     '20' => [
                             'ON',
                             'ON',
                             'OFF',
                             ]
                    },
         'Subj2' => {
                     '1' => [
                             'data1',
                             'data1',
                             ],
                      '2' => [
                             'data2',
                             'data2',
                             ],
                     '20' => [
                             'ON',
                             'ON',
                             'ON',
                             'OFF',
                             'OFF',
                             ]
                    },
        };

sub num_ons_before_first_off
{
    my $n = 0;
    foreach(@_)
    {
        last if $_ eq 'OFF';
        $n++;
    }
    return $n;
}

# store a 'numons' piece of data for each subject, for use later
foreach my $subject(values(%$ALLDATA))
{
    $subject->{'numons'} = num_ons_before_first_off(@{$subject->{'20'}}); 
}

# calculate the max 'numons' across all subjects
my $max_ons = max(map { $_->{'numons'} } values(%$ALLDATA));

foreach my $k(keys(%$ALLDATA))
{
    my $subject = $ALLDATA->{$k};

    #output leading blank entries
    print ',' x ($max_ons - $subject->{'numons'});

    #output the real data
    foreach my $data(@{$subject->{'20'}})
    {
        print "$data,";
    }
    print "\n";
}

希望显而易见的是如何将其扩展到数据输出的其余部分。

你的问题中是否需要间距? 你说它是CSV,所以我猜不是。如果这很重要,我可以更新。

答案 1 :(得分:0)

这不是答案,但不适合评论:

当你说你想'通过LIGHT中的ON / OFF列对齐所有数据'时,你的意思是对齐所有内容吗?

例如,如果您有这些数据:

Subj1,ON,ON,OFF,
Subj2,ON,ON,ON,OFF,OFF,

输出会是这个吗?

Subj1,   ,   , ON,  ON, OFF,
Subj2, ON, ON, ON, OFF, OFF,

或者你想要它是这样的:

Subj1,   , ON, ON, OFF,    ,       <-- Note trailing blank entry
Subj2, ON, ON, ON, OFF, OFF,

也就是说,将第一个“关闭”对齐,就像在文字描述中一样?

答案 2 :(得分:0)

知道了! “max”是那里的关键。 jwd,不知道为什么,但我无法调整你的脚本来处理数据(不断得到“不能使用字符串作为哈希引用,而严格”错误)。但它给了我所需的灵感。

在处理文件时,我实现了一个递增计数器,直到达到第一个OFF。这将作为键值对传递给哈希以及主题ID($ offset {$ id} = $ count)。

完成处理文件后,我将最大值拉出哈希值,然后使用从最大值中减去的原始键值重新定义值。

输出时,我使用您的代码作为基础添加额外的逗号来填充数据。最终相关部分如下所示:

my $max_off = max values %offset;

foreach my $k ( keys %offset ) {
    $offset{$k} = $max_off - $offset{$k};
    }

foreach my $k (sort {$a cmp $b} keys %data) { 
    print OUT $k, "\,";
    print OUT ',' x ($offset{$k});
    foreach my $d ( @{ $data{$k}{$i} } ) { print OUT $d, "\,"; }
        print OUT "\n";
        }
    }

完全符合我的需要。谢谢你的建议!