2013-04-24 76 views
0

我已經構建了幾個腳本來組織來自測試設備的數據輸出,但是我打了一個這個問題的心理障礙。使用散列構建排序數據表

測試設備監測來自多個主體(標識符爲ID1,ID2等)的四種輸入(Data1,Data2,Data3,Data4),並且每個間隔都記錄日期和時間戳。設備傾銷的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保留爲每行中的第一項或者每個主題具有一個文件,這使得它更容易。

注意:因爲時間/日期是在他們自己的行上,所以我爲每個使用了一個變量;如果腳本檢測到包含新時間和/或日期的行,則會更新變量值。

編輯 - 我加入了一些鮑羅廷的建議(離開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 
    } 

繼續獲取垃圾輸出(打印散列引用,而不是實際值!)。自卸車輸出看起來是這樣的:

$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

你的描述很不清楚。整個文件是否只有一個「Subject」?你爲什麼要複製四次?哪些字段可以用作鍵?例如每次都是相同的字符串「Data1」? 'Date'是實際日期還是字符串'Date'?如果可能的話,這將有助於看到一些真實的數據。 – Borodin 2013-04-24 17:12:18

+0

「主題」是數據文件中行中的第一項。我當前的腳本使用它來識別這是包含ID列表的行。示例中的「日期」和「時間」是實際日期和時間戳的佔位符。四種數據類型中的每一種的字符串在每個數據塊中都是相同的,就像上面使用佔位符「數據1」到「數據4」所示的那樣。上面的主題ID由「ID1」,「ID2」等表示 - 將爲了清晰起見而編輯。 – 2013-04-24 17:45:16

回答

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,[...] 
+0

我修改了上面的腳本(它現在逐行讀取)。輸出與期望的相同,比我一直在做的更簡單。但是輸出與我目前的腳本完全一樣:沒有排序。如果我更改了主題ID的順序(例如將ID1移到最後,在ID#之後),則主題行被更改,但數據不會重新排序。 – 2013-04-25 16:34:42

+0

再次,你沒有給我任何工作。什麼是*'ID1','ID2'等等。它們是出現在數據中的文字字符串,還是它們是真實ID的佔位符。如果是後者,那麼身份證採用什麼形式,您希望他們如何分類? – Borodin 2013-04-25 17:50:52

+0

ID(ID1,ID2等)是字母數字,由我以外的人輸入,並且不受我控制。他們是運行測試的人用來識別主題的人。排序旨在爲字母數字(排序{$ a cmp $ b})。請參閱下面的擴展答案 – 2013-04-25 18:49:22

0

你有沒有考慮把數據在SQL數據庫? [例如。 「服務器少」SQLite]

這可能是一種矯枉過正,但它應該爲大數據集提供更好的靈活性。

+0

目標是將腳本打包爲獨立應用程序。我已經爲我們使用的其他四種數據收集系統完成了這項工作。在數據庫中設置它可以防止這種情況發生。 – 2013-04-25 20:41:32

+0

DB_File包提供「btree(file)as hash」是可以接受的嗎?它會給你鍵排序的散列。 – AnFi 2013-04-26 09:45:38