2010-02-03 42 views
0

我正在循環訪問一個大數據文件,並希望檢測每列中的變量類型,例如,如果它是一個Integer或一個浮點數等。 它工作得很好,但是,目前它還是非常基本的,我想補充一點。 到目前爲止,變量的聲明基於數據集的第二行。 (第一個是用作報頭) 這裏是代碼的開頭:循環訪問數據集並處理缺失值

#!/usr/bin/perl 

use warnings; 
use diagnostics; 
use Getopt::Std; 

getopts("i:s:t:") or die "bad options: $!"; 

if($opt_i) { 
open INFILE, "< $opt_i"; 
chomp($headerline = <INFILE>); 
$second = <INFILE>; 
} else { 
die "the input file has to be given\n"; 
} 

if($opt_t) { 
$tablename = $opt_t; 
} else { 
$tablename = $opt_i; 
$tablename =~ s/\.\w+//; 
} 

if($opt_s) { 
$sep = $opt_s; 
} else { 
$sep = ","; 
} 

$headerline =~ s/\"//g; 
$headerline =~ s/\./\_/g; 
@header = split/$sep/, $headerline; 

$second =~ s/\"//g ; 
@second = split/$sep/, $second; 
@terms = split/$sep/, $second; 
@types = split/$sep/, $second; 

現在我已經實施了小環。 問題是我不知道如何處理用NULL聲明的缺失值。目前,循環只是簡單地將變量「012」分配給變量$vartype[$j]

$j = 0; 
while($j <= $#second) { 
if ($types[$j] =~ /NULL/) { 
$vartype[$j] = ""; 
} elsif($types[$j] =~ /[A-Za-z]/) { 
$vartype[$j] = "varchar"; 
} elsif ($types[$j] =~ /\./) { 
$vartype[$j] = "double"; 
} else { 
$vartype[$j] = "int"; 
} 
$j++; 
} 

所以,我怎麼能實現另一個循環結構到現有的循環,這樣,每當我在一列中有NULL值的循環讀取在同一列的下一個值,並這樣做,直到他發現一個號碼或字。

我的數據的樣本是例如:

Country.Name  Time.Name AG.LND.AGRI.ZS NY.GDP.MKTP.CD NE.IMP.GNFS.ZS 
Brunei Darussalam 1960  NULL    1139121335.16 3.46 
Brunei Darussalam 1960  NULL    1677595756.64 0.9 
Brunei Darussalam 1960  NULL    1488339328.59 4.19 
Brunei Darussalam 1961  3.98    1869828587.8  3.14 
Brunei Darussalam 1961  3.98    2346769422.22 3.38 
Brunei Darussalam 1961  3.98    2363109706.3  3.17 

如前所述for循環僅使用在第二行對變量的類型決定。

現在我想實現另一個循環,例如在第三列(AG.LND.AGRI.ZS)中,他會遍歷列,直到他檢測到第一個真實值,在本例中爲3.98。此時,循環識別出標有NULL的缺失值,並分配一個空值。

回答

1

我很難搞清楚你正在嘗試做什麼。假設你正在嘗試根據列內容來猜測列類型,這裏有一個辦法。重要的是當字段爲NULL時不設置任何內容,如果您已經確定了類型,則跳過一個字段,並且一旦確定了所有字段類型,就跳出循環。

#!/usr/bin/perl 

use strict; use warnings; 
use Scalar::Util qw(looks_like_number); 

my @names = split ' ', scalar <DATA>; 
my @types; 

while (<DATA>) { 
    chomp; 
    my @values = split/{2,}/; 

    for my $i (0 .. $#values) { 
     next if defined $types[$i]; 
     my $val = $values[$i]; 
     next if $val eq 'NULL'; 
     if ($val =~ /^[0-9]+\z/) { 
      $types[$i] = 'int'; 
     } 
     elsif ($val =~ /^[0-9.]+\z/ 
       and looks_like_number($val)) { 
      $types[$i] = 'double'; 
     } 
     else { 
      $types[$i] = 'varchar'; 
     } 
    } 
    last unless grep { not defined } @types; 
} 

print "$_\n" for @types; 


__DATA__ 
Country.Name  Time.Name AG.LND.AGRI.ZS NY.GDP.MKTP.CD NE.IMP.GNFS.ZS 
Brunei Darussalam 1960  NULL    1139121335.16 3.46 
Brunei Darussalam 1960  NULL    1677595756.64 0.9 
Brunei Darussalam 1960  NULL    1488339328.59 4.19 
Brunei Darussalam 1961  3.98    1869828587.8  3.14 
Brunei Darussalam 1961  3.98    2346769422.22 3.38 
Brunei Darussalam 1961  3.98    2363109706.3  3.17 

輸出:

varchar 
int 
double 
double 
double
+0

是的,那正是我想要做的。我有幾個數據集,我喜歡加載到數據庫中,我喜歡編寫一個perl腳本,它會自動檢測每個列的類型。因此我不必打開每個數據集並自己瀏覽這些列。 感謝您的幫助,我看看您的答案! – mropa 2010-02-03 17:18:45

+0

@Sinan:Oesor建議使用Text :: CSV模塊。這是否會減少代碼量和推薦方法? – mropa 2010-02-03 18:24:20

+0

@mropa無論您使用的是「Text :: CSV」,它與您的問題或多或少都是正交的。如果數據字段是製表符分隔的(而不是明顯的多個空格),並且這些字段可能包含帶引號的字符串,那麼使用它將使您的生活更輕鬆。它不會減少代碼量。我的代碼的重點是向您展示從字段內容中推導字段類型的邏輯。 – 2010-02-03 18:43:54

2

停止編程像C.

for my $variable (@types) { 
    if ($variable =~ /NULL/) { 
    push(@vartype, undef); 
    } 
    elsif ($variable =~ /[A-Za-z]/) { 
    push(@vartype, "varchar"); 
    } 
    elsif ($variable =~ /\./) { 
    push(@vartype, "double"; 
    } 
    else { 
    push(@vartype, "int"); 
    } 
} 

雖然,對Perl,你真的應該在散列的數據結構存儲相關數據。類似於:

my $data = [ { value => 'NULL', type => undef }, 
      { value => 'a string', type => 'varchar' }, 
      { value => 9.5, type => 'double'}, 
      { value => 30, type => 'int'}, 
      ]; 
+0

水壺,鍋見面;-)。使用'elsif'或'given/when'。見http://perldoc.perl.org/perlsyn.html#Switch-statements – 2010-02-03 15:29:50

+0

其實我認爲這不是我正在尋找的。該代碼仍然只使用數據集的第二行來檢測變量並分配分類。所以不應該在數據集的另一行中另外讀入數據? – mropa 2010-02-03 15:38:35

+0

在我的辯護中,我很少編程if-elsif-else循環,因爲更容易將調度表映射到我的數據集。我的一個平臺是5.8.8,所以我避免了開關。 :/ – Oesor 2010-02-03 15:54:45