perl - 利用 Perl 操作 csv/分号数据

标签 perl csv awk

我正在寻找有关如何在 1 行 AWK 命令不再足够的情况下操作数据的建议。我正在处理多达 1000 多个行和列的数据集。我遇到了定义太多列变量的问题。我认为有一种方法可以使用循环来迭代数组,以可能定义我要计数和求和的列。我正在尝试根据类似于 Excel COUNTIF 和 SUMIF 的键值计算行的计数和总和。

Data Set Example:
Store_Location;Person;Adult_Child;Age;Weight...
LocationA;PersonA;0;50;200
LocationB;PersonB;1;10;100
LocationA;PersonC;1;12;90
LocationA;PersonA;0;50;200

Desired Output: (delimiter is not important)
Store_Location;Count_Of_Adults;Count_of_Children;Sum_of_Age;Sum_of_Weight
LocationA;2;1;112;490
LocationB;0;1;10;100

这是我使用的 AWK 脚本示例:

BEGIN {FS=";"} {print "Store_Location;Count_Of_Adults;Count_of_Children;Sum_of_Age;Sum_of_Weight"}

{
n[$1]++;
C1_[$1] += ($3 == "1" ? 0 : 1);S1_[$1] += $4;column_sum3+=$4
C2_[$1] += ($3 == "0" ? 0 : 1);S2_[$1] += $5;column_sum4+=$5
}
END {
for (i in n) {
  print i,C1_[i],C2_[i],S1_[i],S2_[i]
}
}

我使用a2p将语法转换为perl并做了一些修改(基于使用不同的列):

$base = 20;
while (<>){
    @array = split(/$FS/, $_, -1);


    $n{$array[$base]}++;

    $C1_{$array[$base]} += ($array[21] eq '' ? 0 : 1);
    $C2_{$array[$base]} += ($array[34] eq '' ? 0 : 1);
    $column_count1 += ($array[21] eq '' ? 0 : 1);
    $column_count2 += ($array[34] eq '' ? 0 : 1);
    $S1_{$array[$base]} += $array[21];
    $S2_{$array[$base]} += $array[34];
    $column_sum1 += $array[21];
    $column_sum2 += $array[34];
}
@sorted_keys = sort { $a <=> $b} keys %n;
foreach $i (@sorted_keys){
    print $i,$C1_{$i},$C2_{$i},$S1_{$i},$S2_{$i};

我希望能够做类似的事情,但我试图将我想要求和的列和我想要计数的列放入不同的数组中。例如:@sum_array=[1,6,10,15,30] & @count_array = [1,10,20]。并使用循环创建总和和计数,而无需声明每个输出列。我可以对每一列进行求和和计数,然后打印我需要的列。我在尝试使用散列/数组在 Perl 中编写此代码时遇到了困难。我尝试使用哈希,但无法获得输出格式,因此我不确定这是否是我想要的数据结构。

$n{$array[$base]}{Adult}{count}+= ($array[21] eq 0 ? 0 : 1);
$n{$array[$base]}{Child}{count}+= ($array[21] eq 1 ? 0 : 1);
$n{$array[$base]}{Weight}{sum} += $array[21];
$n{$array[$base]}{Age}{sum}+= $array[34];

编辑: 我认为我的逻辑问题是我不想调用字段名称/列。因为我想对许多字段执行求和和计数。成人 child 比较只是一个例子。我只想在一处列出我想要使用的列。也许解释它的简单方法是,假设输入数据有 100 列。我希望能够灵活地识别我想要分析的列。例如:第 15-30 列我想根据第 1 列中的唯一值获取每列的总和和计数。然后能够修改相同的代码以获取第 15-20 列和 30-40 列的总和。使用 AWK,我可以调出我想要使用的列($2、$3、$4,...),但当列太多时,管理会变得困难。

最佳答案

尚不完全清楚您想要什么,当然也不清楚您所说的“我在定义太多列变量时遇到问题”是什么意思,但这就是我认为您正在尝试做的事情,希望它能让您继续前进走在正确的道路上:

$ cat file
Store_Location;Person;Adult_Child;Age;Weight
LocationA;PersonA;0;50;200
LocationB;PersonB;1;10;100
LocationA;PersonC;1;12;90
LocationA;PersonA;0;50;200

$ cat tst.awk         
BEGIN{ FS=OFS=";" }

NR==1 {
    split($0,nr2nm)
    for (nr=1;nr in nr2nm;nr++) {
        nm2nr[nr2nm[nr]] = nr
    }
    next
}

{
    stores[$nm2nr["Store_Location"]]

    for (nr=3; nr<=NF; nr++) {
        fldName = nr2nm[nr]
        if ( fldName == "Adult_Child" ) {
            fldName = ($nr == 1 ? "Child" : "Adult")
        }
        fldNames[fldName]
        cnt[$nm2nr["Store_Location"],fldName]++
        sum[$nm2nr["Store_Location"],fldName] += $nr
    }
}

END {
    printf "%s", "Store_Location"
    for (fldName in fldNames) {
        printf ";cnt[%s];sum[%s]", fldName, fldName
    }
    print ""
    for (store in stores) {
        printf "%s", store
        for (fldName in fldNames) {
            printf ";%d;%d", cnt[store,fldName], sum[store,fldName]
        }
        print ""
    }
}

$ awk -f tst.awk file
Store_Location;cnt[Weight];sum[Weight];cnt[Child];sum[Child];cnt[Adult];sum[Adult];cnt[Age];sum[Age]
LocationA;3;490;1;1;2;0;3;112
LocationB;1;100;1;1;0;0;1;10

关于perl - 利用 Perl 操作 csv/分号数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19985805/

相关文章:

multithreading - Perl - 同步数据访问

javascript - 来自外部 .csv 或 .txt 文件的 d3.js 云?

oracle - 使用 PL/SQL 读取 csv 文件

python - 如何使用awk替换所有组合中的不同文本 block ?

python 和 ruby​​ 相当于 perls Template::Declare?

linux - 列与制表符而不是空格对齐

regex - 匹配文件中的连续行。

mysql - Bash:Base64 编码非常大的 .csv 中的 1 列并输出到新文件

linux - 如何用字符串和随机生成的数字替换文件中的 header 值?

linux - 使用由另一列中的值定义的滑动窗口对数字列求和