นับผลรวมของแต่ละคอลัมน์ในไฟล์


9

ในไฟล์ที่มีจำนวนคอลัมน์แตกต่างกันคั่นด้วยช่องว่าง '', วิธีนับผลรวมของคอลัมน์ ตัวอย่างจะแสดงความต้องการ:

File A:

1 2 
2 3
4 5 6 
1 1 1 5

จากนั้นผลลัพธ์จะเป็น:

  • สำหรับคอลัมน์ 1 (1 + 2 + 4 + 1) = 8
  • สำหรับคอลัมน์ 2 คือ 11
  • สำหรับคอลัมน์ 3 คือ 7
  • สำหรับคอลัมน์ 4 คือ 5

คำตอบ:


12

การใช้ awk

awk '{for (i=1;i<=NF;i++) sum[i]+=$i;}; END{for (i in sum) print "for column "i" is " sum[i];}' FileA
for column 1 is 8
for column 2 is 11
for column 3 is 7
for column 4 is 5

ใช้งานอาร์เรย์ได้ดี แต่ฉันคิดว่ามันง่ายขึ้นที่จะนับจำนวนและพิมพ์ได้ทันที
Sergiy Kolodyazhnyy

แน่นอนนี่คือคำตอบที่ดีที่สุดที่นี่
kos

5

ใช้numsumสำหรับงานนั้นและแยกระหว่างการประมวลผลข้อมูลและแสดงผลลัพธ์

ติดตั้งnum-utilsเราต้องการnumsum

sudo apt-get install num-utils

และเริ่มด้วย

numsum -c <your_file_name>

ตัวอย่าง

$ cat "File A"
1 2 
2 3
4 5 6 
1 1 1 5

$ numsum -c "File A"
8 11 7 5

หรือด้วยรูปแบบที่คุณต้องการ:

$ numsum -c "File A" | awk '{for(i=1;i<=NF;i++) {print "for column "i" is "$i}}'
for column 1 is 8
for column 2 is 11
for column 3 is 7
for column 4 is 5

จาก man numsum

-c      Print out the sum of each column.

ตัวอย่างจาก man numsum

EXAMPLES

   Add up the 1st, 2nd and 5th columns only.

       $ numsum -c -x 1,2,5 columns
       15 40 115

   Add up the rows of numbers of a file.

        $ numsum -r columns
        55
        60
        65
        70
        75

3
#!/bin/sh

while read a b c d; do
    col1=$((col1 + a))
    col2=$((col2 + b))
    col3=$((col3 + c))
    col4=$((col4 + d))
done < File_A

echo $col1 $col2 $col3 $col4

คุณอาจพูด(( col1 += a ))ได้ ฯลฯecho "..."ก็ปลอดภัยกว่าเช่นกันwhile IFS= read -r ...
fedorqui

@fedorqui echoปลอดภัยที่จะใช้วิธีดังกล่าวเพื่อสะท้อนจำนวน$IFSค่าเริ่มต้นที่ช่องว่างและที่คาดว่าจะเป็นตัวเลขจึงไม่จำเป็นต้องจัดการกับแบ็กสแลช ข้อเสียข้อเดียวของคำตอบนี้คือต้องการทราบจำนวนของคอลัมน์ก่อนดำเนินการ
kos

@kos คุณไม่สามารถรู้ได้ว่าไฟล์อินพุตเป็นอย่างไร และถึงแม้ OP จะกล่าวถึงตัวเลขเพียงอย่างเดียว แต่ก็เป็นการฝึกฝนที่ดีเสมอเพื่อเตรียมรับสิ่งที่เลวร้ายกว่า ดูฉันจะอ่านไฟล์ (data stream, variable) ทีละบรรทัด (และ / หรือ field-by-field) ได้อย่างไร? สำหรับคำอธิบายที่งดงาม
fedorqui

@fedorqui ตามคำสั่งของคุณเองฉันคิดว่านี่เป็นการอภิปราย หากคุณต้องการทำคะแนนโดยสมมติว่าไฟล์อินพุตอาจมีอย่างอื่นที่ไม่ใช่ตัวเลขคุณจะขาดส่วนที่โจ่งแจ้ง: ตรวจสอบว่าสิ่งที่อ่านเป็นตัวเลขหรือไม่ การเพิ่มสตริงและการใช้echo "[...]"เพื่อพิมพ์สิ่งที่คุณไม่ต้องการออกอย่างถูกต้องเหมาะสม
kos

@kos แน่นอนคุณสามารถพูดได้echo $varและwhile read a b cทำงานได้ที่นี่ อย่างไรก็ตามคุณจะได้คุ้นเคยกับการเขียนมันในทางที่อ่อนแอและวันหนึ่งคุณจะได้รับข้อผิดพลาดแปลก ๆ ในขณะที่การประมวลผลไฟล์ที่ซับซ้อนมากขึ้น จากนั้นคุณจะสังเกตเห็นว่าการพูดถึงตัวแปรและการใช้while IFS= read -r ...นั้นปลอดภัยกว่าและจะพูดว่า "ใช่แล้ว fedorqui ถูกต้องฉันหวังว่าฉันจะให้เขาไปกอดเขาเพื่อแสดงความขอบคุณ!"
fedorqui

3

ตัดสินจากความคิดเห็นต่อคำตอบของคุณคุณต้องการผลรวมของคอลัมน์เดียวในแต่ละครั้ง ถ้าเป็นเช่นนั้นต่อไปนี้เป็นวิธีที่ไม่ต้องดำเนินการใด ๆ

cut -d' ' -f3 FileA | grep . | paste -s -d+ | bc

โดยที่คุณจะแทนที่3ด้วยหมายเลขคอลัมน์ที่คุณสนใจ


0

ต่อไปนี้เป็นวิธีสคริปต์ Perl แบบหนึ่งบรรทัด สิ่งนี้อาศัยการใช้-aแฟล็กซึ่งอนุญาตให้แยกบรรทัดที่อ่านโดยอัตโนมัติในขณะนี้พร้อมกับ-nตั้งค่าสถานะเป็นอาเร@Fย์ สิ่งที่เราต้องทำคือวนซ้ำไอเท็มเหล่านั้นและเพิ่มลงในดัชนีตามลำดับใน$sumอาเรย์ดังนั้นประสิทธิภาพของไอเท็มอาเรย์แต่ละตัวจึงเป็นผลรวมสำหรับแต่ละคอลัมน์ ในที่สุดเราจะพิมพ์ผลลัพธ์ภายใน ENDบล็อคโค้ด

$ perl -lane '$j=0;foreach $i (@F){$sum[$j]+=$i; $j+=1;}; END{print join("\n",@sum)} ' input.txt                                                     
8
11
7
5

หรือนี่คือวิธีการสคริปต์ Perl แบบเต็ม มันอาศัยการแยกแต่ละบรรทัดเป็นอาร์เรย์และวนซ้ำแต่ละรายการในอาร์เรย์นั้นเพิ่มจำนวนแต่ละหมายเลขไปยังการครอบครองตามลำดับใน@sumsอาร์เรย์ สคริปต์พิมพ์แต่ละบรรทัดจากนั้นสร้างรายงานสำหรับแต่ละคอลัมน์ การพิมพ์ของแต่ละบรรทัดสามารถลบออกได้โดยเพิ่ม#ก่อนprintf("%s",$line);

#!/usr/bin/env perl
use strict;
use warnings;

open(my $fh,"<",$ARGV[0]); 
my $i = 0;
my @sums;

while(my $line = <$fh>) { 
    printf("%s",$line);
    my @nums = split(" ",$line);
    my $j = 0;
    foreach my $num (@nums){
        $sums[$j] += $num;
        $j += 1;
    }

}

my $k = 0;
foreach my $sum (@sums){
    printf("- column %d sum: %d\n",$k,$sum);
    $k+=1;
}

close($fh);

chmod +x ./sum_columns.pl && ./sum_columns.pl input.txtการใช้งานง่าย ตัวอย่างเช่น:

$ ./sum_columns_2.pl input.txt                                                                                                                       
1 2 
2 3
4 5 6 
1 1 1 5
- column 0 sum: 8
- column 1 sum: 11
- column 2 sum: 7
- column 3 sum: 5

-2

ทางออกที่ง่าย:

awk '{sum += $i} END {print sum}' file

แทนที่ i ด้วยหมายเลขคอลัมน์เช่น column1:

awk '{sum += $1} END {print sum}' file

ผลลัพธ์คือ:

8

3
สิ่งนี้ทำให้คุณได้รับเพียงหนึ่งคอลัมน์ คุณไม่ได้ทำตามข้อกำหนดของคุณเอง
Oli

ฉันไม่ได้ระบุว่าฉันต้องการผลลัพธ์ทั้งหมดในคำสั่งเดียวกัน รวมทั้งคำตอบนี้ต้องใช้วนและมันจะสมบูรณ์แบบ
Maythux

เหตุใดจึงต้อง downvoting
Maythux
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.