2011-11-28 43 views
10

Me gustaría calcular el porcentaje de valor en cada línea entre todas las líneas y agregarlo como otra columna. de entrada (delimitador es \ t):Cómo agregar una columna con porcentaje

1 10  
2 10 
3 20 
4 40 

salida deseada con la tercera columna añadida mostrando porcentaje calculado sobre la base de los valores en la segunda columna:

1 10 12.50 
2 10 12.50 
3 20 25.00 
4 40 50.00 

he tratado de hacerlo yo mismo, pero cuando el total calculado para todas las líneas no supe cómo conservar el resto de la línea sin cambios. ¡Muchas gracias por la ayuda!

Respuesta

12

Aquí tiene, uno pase solución paso a awk -

awk 'NR==FNR{a = a + $2;next} {c = ($2/a)*100;print $1,$2,c }' file file

[jaypal:~/Temp] cat file 
1 10  
2 10 
3 20 
4 40 
[jaypal:~/Temp] awk 'NR==FNR{a = a + $2;next} {c = ($2/a)*100;print $1,$2,c }' file file 
1 10 12.5 
2 10 12.5 
3 20 25 
4 40 50 

Actualización: Si pestaña es un necesario en la producción a continuación, sólo establece la variable OFS a "\ t".

[jaypal:~/Temp] awk -v OFS="\t" 'NR==FNR{a = a + $2;next} {c = ($2/a)*100;print $1,$2,c }' file file 
1 10 12.5 
2 10 12.5 
3 20 25 
4 40 50 

del desbloqueo de patrón {acción} declaraciones:

  • El primer patrón es NR==FNR. FNR es la variable incorporada de awk que realiza un seguimiento del número de registros (por defecto separados por una nueva línea) en un archivo dado. Entonces FNR en nuestro caso sería 4. NR es similar a FNR pero no se reinicia a 0. Continúa creciendo. Entonces NR en nuestro caso sería 8.

  • Este patrón será cierto solo para los primeros 4 registros y eso es exactamente lo que queremos. Después de leer detenidamente los 4 registros, asignamos el total a una variable a. Tenga en cuenta que no lo inicializamos. En awk no es necesario. Sin embargo, esto se rompería si toda la columna 2 es 0. Entonces puede manejarlo colocando una declaración if en la segunda declaración de acción, es decir, haga la división solo si a> 0 else di división por 0 o algo así.

  • next es necesario porque realmente no queremos que se ejecute la instrucción second pattern {action}. next le dice a awk que pare más acciones y pase al siguiente registro.

  • Una vez que se han analizado los cuatro registros, comienza el siguiente patrón {acción}, que es bastante directo. Hacer el porcentaje e imprimir la columna 1 y 2 junto con el porcentaje al lado de ellos.

Nota:Como @lhf se menciona en el comentario, esta sola línea sólo funcionará siempre y cuando usted tiene el conjunto de datos en un archivo. No funcionará si pasa datos a través de una tubería.

En los comentarios, hay una discusión pasando maneras de hacer esta entrada awk one-liner toma de una pipe en lugar de un file. Bueno, la única forma en que podía pensar era almacenar los valores de columna en array y luego usar for loop para escupir cada valor junto con su porcentaje.

Ahora arrays en awk son associative y nunca están en orden, es decir tirando de los valores fuera de las matrices no estarán en el mismo orden en que entraron. Así que si eso es aceptable, entonces el siguiente de una sola línea debería funcionar.

[jaypal:~/Temp] cat file 
1 10  
2 10 
3 20 
4 40 

[jaypal:~/Temp] cat file | awk '{b[$1]=$2;sum=sum+$2} END{for (i in b) print i,b[i],(b[i]/sum)*100}' 
2 10 12.5 
3 20 25 
4 40 50 
1 10 12.5 

Para conseguir el fin, se puede canalizar el resultado de sort.

[jaypal:~/Temp] cat file | awk '{b[$1]=$2;sum=sum+$2} END{for (i in b) print i,b[i],(b[i]/sum)*100}' | sort -n 
1 10 12.5 
2 10 12.5 
3 20 25 
4 40 50 
+0

Esto es todo. ¡Gracias! – Martin

+0

No hay problema. :) Pondré algunas explicaciones para referencia. –

+1

Agradable, pero en realidad no es de una sola pasada. En particular, no se puede usar como filtro, es decir, leyendo de stdin. – lhf

1

Tiene que escapar como %%. Por ejemplo:

printf("%s\t%s\t%s%%\n", $1, $2, $3) 
+0

Gracias, lo siento si no explicarlo correctamente en la cuestión - no tengo problema con signo% (no hay que necesito), mi problema es cómo calcular el valor en sí. – Martin

+0

Oh ... ¡bien! ¡Perdón por haber expresado mal el problema! – jsalonen

2

usted puede hacerlo en un par de pases

#!/bin/bash 

total=$(awk '{total=total+$2}END{print total}' file) 
awk -v total=$total '{ printf ("%s\t%s\t%.2f\n", $1, $2, ($2/total)*100)}' file 
+0

Gracias. Esto también funciona, sin embargo, encontré la solución sugerida por Jaypal más fácil de usar, así que seleccioné su solución como la respuesta. – Martin

0

Tal vez hay una mejor manera, pero me lo pase archivo dos veces.

contenido de 'archivo de entrada':

1  10 
2  10 
3  20 
4  40 

contenido de 'script.awk':

BEGIN { 
     ## Tab as field separator. 
     FS = "\t"; 
} 

## First pass of input file. Get total from second field. 
ARGIND == 1 { 
     total += $2; 
     next; 
} 

## Second pass of input file. Print each original line and percentage as third field. 
{ 
     printf("%s\t%2.2f\n", $0, $2 * 100/total); 
} 

Ejecutar la secuencia de comandos en mi máquina Linux:

gawk -f script.awk infile infile 

Y resultado:

1  10  12.50 
2  10  12.50 
3  20  25.00 
4  40  50.00 
+0

Gracias. Esto también funciona – Martin

Cuestiones relacionadas