Bash | Sortieren (hoch/niedrig) von Werten

Heute ist Stammtischzeit:
Jeden Donnerstag 20:30 Uhr hier im Chat.
Wer Lust hat, kann sich gerne beteiligen. ;)
  • Hallo,

    ich habe eine CSV-Datei mit 5 Werten pro Zeile und insgesamt 30 Zeilen.

    Beispiel:

    Code
    Wert1,Wert2,Wert3,Wert4,Wert5
    20,17,33,112,18
    21,18,32,118,18
    22,17,33,122,17
    21,16,34,131,18
    19,15,35,134,19
    usw.

    Wie kann ich z.B. von Wert2 den niedrigsten und höchsten Wert herausbekommen?

    Bei Dateien, die nur einen Wert pro Zeile haben, ist das mit sort und tail recht einfach durchzuführen. Aber hier sitzt der Wert2 mittendrin.

    Viele Grüße,

    Peter

  • Das mit den hohen und tiefen Werten klappt wunderbar.

    Nun bin ich auf das nächste Problem gestoßen. Ich würde gerne für einen Wert in einer CSV-Datei den Durchschnitt ausrechnen lassen.

    Das ist meine Codezeile:

    Code
    echo $(bc -l <<< "scale=1; ($(cat "daten.csv" | awk -F',' '{sum+=$1}END{print sum}')/$(cat "daten.csv" | wc -l))")

    Mit cat hole ich den Inhalt, schicke ihn zu awk, der eine Summe daraus bildet und mit bc teile ich das durch die Anzahl der Zeilen.

    Grundsätzlich klappt das. Aber awk frisst mir die Werte hinter dem Komma (hier Punkt). Bei der ersten Spalte rechnet er 33+33+33+33+33, somit Durchschnitt 33.

    Irgendwelche Ideen?

    Beispielinhalt der daten.csv:

    Code
    33.2,26.7,29.9
    33.3,26.8,30.4
    33.4,26.9,30.3
    33.6,27.1,30.4
    33.9,27.4,31.1
    usw.

    Viele Grüße,

    Peter

  • Nachtrag:

    Ich bin gerade darauf gestoßen, dass awk sich am Punkt stoßen könnte.

    Als erste Notlösung habe ich mit tr das Kommas in Semikolons ersetzt, sowie dann die Punkte in Kommas, nach dem awk wiederum alles zurück.

    Code
    echo $(bc -l <<< "scale=1; ($(cat "daten.csv" | tr "," ";" | tr "." "," | awk -F',' '{sum+=$1}END{print sum}' | tr "," "." | tr ";" ",")/$(cat "daten.csv" | wc -l))")

    Das sieht schon sehr verwegen aus. *lach*

    Gibt es eine elegantere Lösung?

    Viele Grüße,

    Peter

    Einmal editiert, zuletzt von Peter0311 (5. Juli 2022 um 13:38)

  • Code
    awk -F',' 'BEGIN {min = 9999} NR > 1 {++count;value = $2;sum += value; if(value < min){min = value} if( value > max){max=value;}} END { print min " " max " " sum / count }' inputfile

    Alles auf einer Zeile!

    Man kann es auch auf mehrere Zeilen schreiben, aber dann muss man beim Quoting aufpassen.

    - - - - -

    awk sieht Punkte als Dezimaltrenner das ist problemlos.

    Die Kommandozeile nimmt an dass das File einen Header hat. Falls das nicht der Fall ist NR > 1 loeschen

  • jftr: Sowohl awk als auch wc können Dateien direkt lesen - "Useless use of cat".

    Wenn du nichts zu sagen hast, sag einfach nichts.

    Einmal editiert, zuletzt von llutz (5. Juli 2022 um 14:01)

  • Die awk Loesung ist definitiv die kuerzeste und lesbarste Version :thumbup: . Aber Ihr kennt mich ja: Ich nutze gerne bash :shy: . Anbei meine Loesung wie ich es in bash schreiben wuerde:

    Code
    ./csv.sh csv.dat 
    Sum:454.4 Cnt:15 Avg:30.2
  • > Die awk Loesung ist definitiv die kuerzeste und lesbarste Version

    Noeh:

    Code
    python3 -c 'import pandas as pd; print( pd.read_csv("input.csv",header=None)[1].describe() )'

    Und wenn er wirklich nur den Mittelwert will und einen Header hat, wird es noch klarer:

    Code
     python3 -c 'import pandas as pd; print( pd.read_csv("input.csv")["Wert2"].mean() )'

    2 Mal editiert, zuletzt von Tell (5. Juli 2022 um 19:25)

  • Eine Library zu nutzen verkürzt natürlich alles sehr und ist unfair

    awk ist auch nur ein Programm, dass mit der Bash genutzt wird.

    Für Python gibt es auch ein csv-Modul, dass mit in der Standardbibliothek enthalten ist.

    Wenn man Python anwendet, will man sich eigentlich wohlfühlen und schönen Code sehen.

    One-Liner sind nicht so schön.

    Hier mal mein Versuch mit CSV:

    Soll aber keine Bekehrung zu Python sein, sondern nur aufzeigen, dass die Datenverarbeitung mit Python einfacher ist.

    Wenn man dann noch Pandas lernt, kann man quasi zaubern (z.B. CSV direkt aus dem Internet herunterladen und öffnen mit einer Funktion).

  • Wenn man Python anwendet, will man sich eigentlich wohlfühlen und schönen Code sehen.

    Verstehe mich nicht falsch. Ich bin da ganz bei Dir. Ich habe auch lange genug in Python programmiert. Python ist wesentlich maechtiger als bash. Ich will hier auch keine Diskussion bash vs Python anzetteln. Eine jede Programmiersprache hat seine Staerken und EInsatzgebiete. Und man kann auch mit jeder Programmiersprache ein endliches Problem loesen - und wenn es Assembler oder die Touringmaschine ist :lol:

    Der TE hat bash genutzt weil er sich damit offensichtlich auskennt. Jetzt awk zu nutzen mag fuer ihn schon ein gewisser Quantensprung sein. awk ist eben anders als bash. Ich wollte einfach dem TE nur zeigen wie man es auch in bash hinbekommen kann :)

  • Ich wollte einfach dem TE nur zeigen wie man es auch in bash hinbekommen kann

    Threadüberschrift ist

    Zitat

    Bash | Sortieren (hoch/niedrig) von Werten

    Wenn einer ein günstiges Fahrrad sucht gebe ich ja auch keinen Tipp, wo er ein günstiges Auto bekommt.

    :2cents:

  • Wenn man nur einen Hammer hat, dann ist jedes Problem ein Nagel.

    Klar, man kann sich selbst zwingen, alle Probleme mit einem Hammer zu lösen.

    Genau das ist der Grund, wieso es viele Sprachen gibt.

    Was ist an dem Hinweis und einem passenden Beispiel falsch?

  • Was ist an dem Hinweis und einem passenden Beispiel falsch?

    Falsch ist daran gar nichts

    Wenn der TE aber gerade dabei ist, die Problematik mit bash zu lösen, weil er da vielleicht ein paar Vorkenntnisse hat, verwirrt ihn der Rest womöglich.

  • Ich finde auch, dass man bei Shell-Skripten durchaus andere Programmiersprachen einwerfen kann, denn AWK ist ja auch eine Programmiersprache, und `sort`, `cut` & Co ist auch nicht Shell sondern schon ein externes Werkzeug.

    Für CSV-Dateien verwende ich ganz gerne die Programme aus dem csvkit.

    Anzeigen:

    Statistik für Spalten:

    Statistik als CSV und auf die Spalten "Wert2" und 5 beschränkt:

    Code
    $ LC_ALL=C csvstat -c "Wert2,5" --csv test.csv
    column_id,column_name,type,nulls,unique,min,max,sum,mean,median,stdev,len,freq
    2,Wert2,Number,False,4,15,18,83,16.6,17,1.14,,"17, 18, 16, 15"
    5,Wert5,Number,False,3,17,19,90,18,18,0.707,,"18, 17, 19"

    Nur Spalte "Wert2" und aus dem Ergebnis nur die Spalten "min", "max", und "mean", und mit Tabs als Trenner:

    Code
    $ LC_ALL=C csvstat -c "Wert2" --csv test.csv | csvcut -c "min,max,mean" | csvformat --out-tabs
    min     max     mean
    15      18      16.6

    Und das ganze in ein Skript eingebaut um drei Variablen damit zu bestücken:

    Alternativ könnte man mit ``csvsql`` die Datei per SQL abfragen:

    Code
    $ csvsql test.csv --query "SELECT min(Wert2), max(Wert2), avg(Wert2) FROM test"
    min(Wert2),max(Wert2),avg(Wert2)
    15.0,18.0,16.6

    Und noch eine Python-Alternative zu Pandas:

    Code
    $ python3.8 -c 'from agate import *;print(*Table.from_csv("test.csv").aggregate([(x,x("Wert2")) for x in [Min,Max,Mean]]).values())'
    15 18 16.6

    “Dawn, n.: The time when men of reason go to bed.” — Ambrose Bierce, “The Devil's Dictionary”

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!