Script que gera a média de várias linhas de grupo, de um arquivo

4

Esta é uma parte "Pequena" do arquivo car_sales.txt que estou tentando extrair informações (o arquivo completo tem cerca de 700 linhas, com mais marcas de veículos do que as listadas aqui):

first_name,last_name,price_paid,brand,year Mann,Mathers,20500.79,Chevy,2012 Doug,Samual,21000.12,Dodge,2015 Walter,Gray,17000.87,Dodge,2010 Jessica,Garnet,17350.00,MINI,2009 Paula,Raymond,45300.87,BMW,2015 Willie,Reynolds,64950.05,BMW,2015 Sam,Collins,70200.35,Lexus,2014 Katy,Martinez,29580.84,Chevy,2012 Nicole,Davis,31650.60,Chevy,2009 Brenda,Gray,12400.56,Dodge,2012 Samantha,Fernandez,27900.21,MINI,2015 Eric,Woods,68900.85,BMW,2009 George,Luke,33453.91,BMW,2011 Mildred,Takey,46820.80,Lexus,2012

Eu gostaria de produzir as colunas "brand" e "price_paid" (e encontrar o preço médio pago por marca, para todos os carros), classificado (a-z) e a primeira linha do "cabeçalho" removida. Esta é a saída que estou procurando (do exemplo listado acima):

BMW,53151.4 Chevy,27244.1 Dodge,16800.5 Lexus,58510.6 MINI,22625.1

Agora eu tenho trabalhado nisso tentando descobrir isso sem sorte por 2 dias (eu sou novo nisso), o melhor que eu posso fazer é isto:

sed '1d' car_sales.txt |awk -F ',' '/Chevy/{print $3}' $1|awk '{total += $1; count ++}END{print "Chevy," total/count}'

Agora, claramente, isso é "não" o que estou procurando; Se eu precisasse de uma saída média para apenas "um single" Brand / Price_Paid, então sim, funcionaria, gostaria apenas de inserir o single "Pattern" que estou procurando e então eu teria o preço médio pago.

No entanto, estou procurando uma maneira de capturar e exibir o preço médio pago por TODAS as marcas no arquivo car_sales.txt. E há muitas outras marcas, apenas as 5 na parte que listei (mais de 50 marcas).

Eu li meus 3 livros que eu tenho e escaneei por horas online, e pela minha vida eu não consigo entender. Talvez eu nem esteja olhando no lugar certo, achei que o awk seria a resposta, mas tão grande. Muito obrigado pela ajuda antecipada.

Eu então tive uma idéia pensando que havia descoberto uma maneira de fazer isso acontecer e começar a escrever este roteiro. Logicamente, parecia que iria funcionar na minha cabeça, o pensamento era que eu usaria a saída da primeira função como a saída para o segundo. Infelizmente, isso não funcionou, eu pensei que estava no caminho certo, mas não.

#!/bin/bash

#This will output the car "brand"
function brand {
        sed '1d' $1| cut -d ',' -f 4 |sort|uniq 
}

#The output of function "brand", will be the pattern for function "average"
function average {
    awk -F ',' '/'"$names"'/{print $3}' $1|awk '{total += $1; count ++}END{print "'$names'" "," total/count}'

}

brand $1
names=$(brand)
average $1 $names
    
por Jaymes Deen 30.11.2015 / 05:25

1 resposta

5

Como awk arrays são indexados por strings, você pode usar uma matriz para manter o preço total dessa marca até o momento e usar outra matriz para manter a contagem de registros vistos para essa marca.

Como "marca" é o campo 4, você pode indexar as matrizes em awk da seguinte forma:

total_price[$4] += $3        # accumulate total price for this brand
count[$4] += 1               # increment count of records for this brand

No final, percorra as teclas dos arrays e formate a saída enquanto calcula as médias.

Como POSIX awk não contém nenhuma função de classificação, canalize a saída do comando awk para o comando padrão% Unixsort.

Por favor, tente isto:

Script

#!/bin/sh

#first_name,last_name,price_paid,brand,year
#print for each brand, the average price paid

awk -F, '
    NR == 1 {
        next                        # skip header
    }
    {
        price_paid[$4] += $3        # accumulate total price for this brand       
        count[$4] += 1              # increment count of records for this brand
    }
    END {
        for (brand in price_paid) {
            printf "%s,%7.2f\n", brand, price_paid[brand] / count[brand]
        }
    }
' < "${1:?filename required}" | sort

Anotação / explicação

  1. Invoque o comando awk , defina o Separador de campos como vírgula ( , ) e passe tudo entre aspas simples nesta linha e as próximas cotações simples várias linhas abaixo, como o script:

    awk -F, '
    
  2. Ignorar Cabeçalho: Se o número do registro atual for 1, ignore todo o processamento na linha atual (a primeira linha) e obtenha a próxima linha de entrada:

        NR == 1 {
            next                        # skip header
        }
    
  3. Acumular preço total por marca (isso é executado em todas as linhas):
    As matrizes price_paid e count são indexadas pela cadeia brand .
    Adicione o preço atual pago ( $3 ) ao total price_paid dessa marca.
    Incrementar a contagem de registros para essa marca:

        {
            price_paid[$4] += $3        # accumulate total price for this brand    
            count[$4] += 1              # increment count of records for this brand
        }
    
  4. Imprimir a tabela de saída: depois que todas as entradas forem processadas, percorra as chaves ( brand ) até a matriz price_paid e, para cada brand , imprima brand e a média price_paid que brand :

        END {
            for (brand in price_paid) {
                printf "%s,%7.2f\n", brand, price_paid[brand] / count[brand]
            }
       }
    
  5. Encerre o argumento de script, redirecione a entrada do parâmetro filename e canalize a saída do comando awk para o comando sort :

    ' < "${1:?filename required}" | sort
    

A aspa simples ( ' ) encerra o argumento de script como awk .
< "${1:?filename required}" redireciona a entrada padrão de awk do nome do arquivo especificado pelo primeiro parâmetro de linha de comando para o script. Se não houver nenhum parâmetro, o shell imprimirá uma mensagem de erro contendo "filename required" e sairá com status de erro.

    
por 30.11.2015 / 07:40