Como extrair o nome da coluna (cabeçalho) de um arquivo CSV que contém o valor máximo em uma linha?

2

Eu estou tentando extrair o nome da coluna com o valor máximo em cada linha usando o script bash, ou seja, o valor do cabeçalho da coluna ou o valor da mesma coluna na primeira linha. Estou usando o seguinte para extrair o valor máximo de cada linha em um arquivo CSV, mas não consigo descobrir como imprimir o nome da coluna junto com o valor máximo:

awk -F ',' '{max=$'$col1';for (i=1;i<=NF;i++) {if ($i > max){max=$i}};print " max: " max}' "$INPUT_PATH/tmp.csv" >>$INPUT_PATH/max1.csv

Exemplo:

Exemplo de dados CSV:

col1,col2,col3,col4
1,5,2,6
4,0,1,2
1,2,0,0
0,0,7,0

Saída desejada:

col4 6 2
col1 4 1
col2 2 2
col3 7 3

Existe uma maneira de fazer isso no comando acima ou há uma maneira melhor de extrair as informações desejadas do arquivo CSV?

    
por Ankit Vashistha 23.07.2015 / 10:12

4 respostas

0

Seu futuro (e qualquer outra pessoa que tenha que manter o software) agradecerá se você usar uma linguagem como Python para isso. Claro que não vai ser um one-liner, mas pelo menos é legível O pseudo-código Naive é algo assim (completamente não testado):

import csv
import defaultdict

with open('max1.csv') as file_handle:
    csv_reader = csv.reader(file_handle)
    headers = csv_reader.next()
    maxes = defaultdict(0) # Or negative infinity
    for values in csv_reader:
       for index in range(len(values)):
           if value > maxes[headers[index]]:
               maxes[headers[index]] = value
    
por 23.07.2015 / 10:46
0

É um pouco claro o que você está perguntando, eu suponho que você deseja imprimir para cada valor máximo de linha do cabeçalho de linha e coluna para coluna em que este valor é encontrado:

BEGIN {
    FS = ",";
}
NR == 1 {
    for (i = 1; i <= NF; i++) {
        x[i] = $i;
    }
    next;
}
{
    max = $1 + 0;
    for (i = 1; i <= NF; i++) {
        if (max <= ($i + 0)) {
             v[x[i]] = $i + 0;
             max = (v[x[i]] >= max) ? v[x[i]] : max;
        }
    }

    printf("Row %d: Column(s): ", NR);
    for (i in v) {
        if (max == v[i])
            printf("%s ", i);
    }
    print "max value: " max;
}

Você pode salvar acima em file.awk e executar:

awk -f file.awk your input

Assim, para determinada entrada:

col1,col2,col3,col4,col5,col6,col7,col8
-1,-2,-22,-4,-1,-2,-4,-8
-9,-3,-2,-1,-2,-4,-5,-7
0,-3,-2,-1,-10,-11,-2,-8

O resultado deve ser:

Row 2, Colums(s): col1 col5 max value: -1
Row 3, Colums(s): col4 col5 max value: -1
Row 4, Colums(s): col1 max value: 0
    
por 23.07.2015 / 12:44
0

O seguinte permite um máximo de repetição na mesma linha.

awk -F, 'NR==1 { split($0,head,FS); next }
         { max=0; delete a; 
           for(i=1;i<=NF;i++) if($i>=max){ max=$i; a[max]=a[max]head[i]" ("i"), " }
           print "max " max "\t" substr(a[max], 0, length(a[max])-2)
         }' file

entrada:

hdr A,hdr B,hdr C,hdr D,hdr E,hdr F
5,2,7,4,7,-9
1,5,4,3,2,1
1,5,9,9,5,3

saída:

max 7   hdr C (3), hdr E (5)
max 5   hdr B (2)
max 9   hdr C (3), hdr D (4)
    
por 23.07.2015 / 11:36
-1

O problema com o CSV é que ele não funciona bem com as ferramentas normais do shell. Eles simplesmente não fazem isso muito bem. Pode ser feito em casos triviais, mas na verdade - uma linguagem de script é a ferramenta para o trabalho.

Eu estaria pensando mais perl pessoalmente:

#!/usr/bin/env perl
use strict;
use warnings;
use Text::CSV;

my $csv = Text::CSV->new();

open ( my $input, "<", "your_file.csv" ) or die $!;
$csv->column_names( $csv->getline( $input ) );

while ( my $row = $csv->getline_hr( $input ) ) {
    my ( $highest, @rest ) = sort { $row->{$b} <=> $row->{$a} } keys %$row;
    print join( "\t", $highest, $row->{$highest} ), "\n";
}

Qual se usar como entrada:

first,second,third,fourth
1,3,4,5,
5,4,3,2,
1,1,4,1,

imprimirá:

fourth  5
first   5
third   4
    
por 23.07.2015 / 11:24