staging.inyokaproject.org

Mittelwert u. Standardabweichung mehrerer Spalten in csv-Dateien mit awk berechnen

Status: Ungelöst | Ubuntu-Version: Ubuntu 16.04 (Xenial Xerus)
Antworten |

Mojo_Dodo

Anmeldungsdatum:
31. Januar 2008

Beiträge: 212

Hallo zusammen, da ich einige csv-Dateien auswerten muss, wollte ich mir mit awk das Leben etwas leichter machen, was bisher aber noch nicht ganz geklappt hat ☺

Ich habe mehrere csv-Dateien, welche alle folgendermaßen aufgebaut sind:

Datei1.csv
1
2
3
4
5
6
Spalte1,Spalte2,Spalte3
1,10.1,150
2,15.5,160
3,16.7,170
4,19.8,180
5,20.1,190

Nun muss ich für jede Datei von jeder Spalte den Mittelwert und die Standardabweichung berechnen und hätte die Ergebnisse gerne gesammelt in einer neuen Datei, z.B. so:

1
2
3
4
Dateiname,MW Spalte1,Stabw Spalte1,MW Spalte2,Stabw Spalte2,MW Spalte3,Stabw Spalte3
Datei1.csv,3.0,1.58,16.4,4.1,170.0,15.81
Datei2.csv,
Datei3.csv,

Ich habe so angefangen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
awk -F',' 'BEGIN { OFS = "," } {
for(i=1; i<=NF; i++)
{ sum[i] += $i;
}}

END {
{ OFS = "," }
for(i=1; i<=NF; i++){
	mean = sum[i]/(NR);
	if(i<NF){
		printf mean
	}else{
		print mean}
        };
     }' Pfad/*.csv >> Pfad/Ergebnisse.csv

und habe dazu nun folgende Fragen:

  1. Die Ausgabe in Zeilenform funktioniert bereits, aber es fehlen die Trennzeichen. Was habe ich mit OFS = "," falsch gemacht?

  2. Beim Berechnen des Mittelwerts nimmt awk die erste Zeile mit der Beschriftung als 0. Das lässt sich mit sum[i]/(NR-1) zwar beheben, aber gibt noch eine bessere Lösung dafür?

  3. Der awk-Befehl soll ja auf alle Dateien angewendet werden. Pfad/*.csv >> Pfad/Ergebnisse.csv führt aber nicht dazu, dass die Ausgabe der nächsten Datei in einer neuen Zeile landet. Das wollte ich erreichen indem ich am Ende print, anstatt printf genommen habe.

  4. Für die Standardabweichung bin ich mir sicher an welcher Stelle ich die Formel einfügen muss? Hinter sum[i] += $i; ist es ja noch nicht möglich, weil ich den Mittelwert noch nicht habe.

Über Hinweise jeglicher Art bin ich sehr dankbar!

Es muss übrigens nicht unbedingt awk sein. Falls es sinnvoller ist, können auch alle anderen Bash-Bordmittel zum Einsatz kommen.

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17630

Ich bin nicht geübt mit awk, daher kommen sicher noch bessere Vorschläge - meinen ist mit gesundem Misstrauen zu begegnen.

a) gehört das Script in eine Datei, etwa stdabw.awk:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/awk
# usage: awk -F',' ' $0 FILE
#
BEGIN { OFS = "," } {
    for(i=1; i<=NF; i++)
    {
        sum[i] += $i;
    }
}
END {
    {
        OFS = ","
    }
    for(i=1; i<=NF; i++) {
	    mean = sum[i]/(NR);
	    if (i<NF) {
		    printf mean" "
	    } else {
		    print mean }
    };
}

Der Shebang ist ungetestet, dem besonders misstrauen. Das -F kann man bestimmt in den Beginn-Block integrieren.

Multiple Dateien verarzten:

1
2
3
for f in D*csv; do awk -F',' -f stdabw.awk $f ; done
2.5 13.7 141.667
2.5 13.7 141.667

Zweimal dasselbe, weil ich die Datei einfach kopiert habe.

Dunkel erinnere ich mich, an eigenen Trouble mit OFS.

1
 printf mean" "

Das setzt manuell einen Trenner. Komma müsste auch funzen.

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17630

Nach kleinen Tests mit IFS, OFS und FS:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
BEGIN { FS=","} {
    for(i=1; i<=NF; i++)
    {
        sum[i] += $i;
    }
}
END {
    for(i=1; i<=NF; i++) {
	    mean = sum[i]/(NR);
	    if (i<NF) {
		    printf mean", "
	    } else {
		    print mean
	}
    }
}

Aufruf dann:

1
for f in D*csv; do awk -f stdabw.awk $f ; done 

Und Ausgabe in Datei:

1
for f in D*csv; do awk -f stdabw.awk $f ; done > Pfad/Ergebnisse.csv

Mojo_Dodo

(Themenstarter)

Anmeldungsdatum:
31. Januar 2008

Beiträge: 212

Zuerst mal vielen Dank für die Hilfe!

Ich hab noch etwas rumprobiert und das Skript sieht nun so aus:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
BEGIN { FS="," } { OFS="," } {
    for(i=1; i<=NF; i++)
    {
        sum[i] += $i;
    }
}
END {

    for(i=1; i<=NF; i++) {
	    mean = sum[i]/(NR-1);
	    if (i<NF) {
		    printf "%s%s", mean,OFS
	    } else {
		    print mean
	}
    }
}

Was ich nicht wusste: die Variablen OFS und ORS haben keinen Einfluss auf printf, sondern nur auf print - siehe hier im gawk manual: https://www.gnu.org/software/gawk/manual/html_node/Basic-Printf.html#Basic-Printf

Ich habe es jetzt mit printf "%s%s", mean,OFS gelöst. d.h. in greife mit printf einfach auf die Variable OFS zurück. Es funktioniert, aber ob das der sinnvollste Weg ist, weiß ich nicht.

Mal eine andere Frage: mit welchem Editor bekomme ich denn automatisch eine so übersichtliche Darstellung des Codes wie in dem oben geposteten Beispiel?

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17630

Mojo_Dodo schrieb:

Mal eine andere Frage: mit welchem Editor bekomme ich denn automatisch eine so übersichtliche Darstellung des Codes wie in dem oben geposteten Beispiel?

Was meinst Du mit oben geposteten Beispiel?

Mojo_Dodo

(Themenstarter)

Anmeldungsdatum:
31. Januar 2008

Beiträge: 212

user_unknown schrieb:

Mojo_Dodo schrieb:

Mal eine andere Frage: mit welchem Editor bekomme ich denn automatisch eine so übersichtliche Darstellung des Codes wie in dem oben geposteten Beispiel?

Was meinst Du mit oben geposteten Beispiel?

Mein anfangs geposteter Code war nicht sehr übersichtlich formatiert. Die von dir korrigierte Variante hingegen schon. Hast du die geschweiften Klammern, usw. "manuell" übersichtlich eingerückt oder nutzt du einen Editor der das automatisch macht?

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17630

Das ist ja schon ne Woche her.

Hab's wahrscheinlich in den Editor gepackt, um es einzurücken, um überhaupt die Struktur erkennen zu können.

Da wohnt so ein Formatiertier in mir, das macht das schon halbautomatisch.

Antworten |