循环遍历数据集并处理缺失值

时间:2010-02-03 14:45:33

标签: perl loops

我正在遍历一个大数据文件并喜欢检测每列中的变量类型, 例如,如果它是Intenger或Float等。 它工作得很好,但是,它仍然是非常基本的,我想添加另一个想法。 到目前为止,变量的声明基于数据集的第二行。 (第一个用作标题。) 这是代码的开头:

#!/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声明的缺失值。此时,循环只是为变量$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的缺失值,并且只分配一个空值。

2 个答案:

答案 0 :(得分: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'},
           ];

答案 1 :(得分: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
相关问题