• Willkommen im Linux Club - dem deutschsprachigen Supportforum für GNU/Linux. Registriere dich kostenlos, um alle Inhalte zu sehen und Fragen zu stellen.

[gelöst] mit gawk Liste auswerten?

hambam

Newbie
Hallo,

ich habe eine größere Liste, die folgende Informationen enthält:

Datum Verkäufer Schuhmarke Preis

Ich würde jetzt gerne einmal diese Liste auswerten nach: Top 5 der Verkäufer mit Summe Preis Top 5 der Schuhmarke mit Summe Preis
Zudem würde ich aber gerne noch Pro Top Verkäufer die Top 5 der Schuhe sehen.

Top 5 der Verkäufer bspw. müsste doch über folgenden Code funktionieren:
Code:
entfernt, da total fehlerhaft

Aber wie bekomme ich die Verknüpfung zu den Schuhen hin?
Also wieviel der Verkäufer pro Schuhmarke verkauft hat?

Vielleicht kann mir hier jemand helfen? Bin leider noch etwas neu, was awk-Programmierung betrifft.
 
OP
H

hambam

Newbie
Das erste Script lief ist nicht fehlerfrei, hier eine schonmal halbwegs funktionierende Variante, die zumindest schonmal die Statistik pro Verkäufer ermittelt:

Code:
#!/bin/sh
#

awk '
{
        datum = $1
        verkaeufer = $2
        marke = $3
        preis = $4
        gesamt += preis
        tsn[verkaeufer] = verkaeufer
        tsa[verkaeufer] +=1
        tsu[verkaeufer] += preis
}
END {
        print "----------------------------------"
        print_it("Statistik","sort",tsa)
        print " "
        print "Gesamt:"gesamt
        print "----------------------------------"
}

function print_it(title, filter, tr) {
        print ""
        printf "%-25.25s ", title
        for (c in tr) {
                print " "
                print "Name: "tsn[c]
                print "Anz Verkaeufe: "tr[c]
                print "Summe Verkaeufer: "tsu[c]
                print " "
        }
        close (filter)
}


Nachwievor hab ich noch keine so rechte Idee, wie ich die Verbindung zwischen Verkäufer und den verkauften Schuhen aufbauen könnte.
Keiner eine Idee?
 
OP
H

hambam

Newbie
Asort habe ich mir auch schon angeschaut, aber das Problem ist, dass ich ja verschiedene Arrays habe und wenn ich ein Array sortiere, verliere ich doch den Bezug zu den anderen.

Hier mein derzeitiger unsortierter Code... Der müsste jetzt "nur" noch nach dem Verkäufer mit dem größten Umsatz und innerhalb mit den meist verkauften Schuhen sortiert werden:
Code:
#!/bin/sh
#

awk '
{
        datum = $1
        verkaeufer = $2
        marke = $3
        preis = $4
        gesamt += preis
        tsn[verkaeufer] = verkaeufer
        tsa[verkaeufer] +=1
        tsu[verkaeufer] += preis
        tsmv[verkaeufer,marke] = verkaeufer
        tsma[verkaeufer,marke] += 1
        tsmn[verkaeufer,marke] = marke
}
END {
        print "----------------------------------"
        print_it("Test","sort",tsa)
        print " "
        print "----------------------------------"
        print "Gesamt:"gesamt
}

function print_it(title, filter, tr) {
        print ""
        print title
        print " Name        Anz        Summe"
        print "-----------------------------"
        for (c in tr) {
                print " "
                printf "%-10s %6d %12.2f\n", tsn[c], tr[c], tsu[c]
                for (d in tsma) {
                        if (tsn[c] == tsmv[d]) {
                                printf "%15s %6d\n", tsmn[d], tsma[d]
                        }
                }
        }
        close (filter)
}

Und hier auch meine kleine Testdatei:

Code:
18.08.2010 smith Nike 100
18.08.2010 smith Addidas 150
18.08.2010 wesson Nike 90
18.08.2010 smith Puma 120
18.08.2010 smith Puma 80
18.08.2010 wesson Nike 110
18.08.2010 wesson Addidas 100
18.08.2001 smith Puma 100
 

spoensche

Moderator
Teammitglied
Also dein Vorhaben, geht aufgrund der Relationen eher in das Gebiet der relationalen Datenbanken und gehört da auch hin. Zum einen sind die Daten leichter zu modellieren (Datenmodell) und zum anderen sind die Abfragen wesentlich effizienter. So müsstest du selber noch einen Algorithmus für Primärschlüssel, Fremdschlüssel entwickeln.
 
OP
H

hambam

Newbie
Leider handelt es sich hier um ein uraltes System, das die Daten eben nur in dieser Form liefert und ich diese auswerten möchte, ohne dass ich die Daten erst wieder in eine Datenbank schaufeln muss, um dort Abfragen zu generieren. Mit der Abfrage würde es dann sehr schnell und quasi in Echtzeit funktionieren.

Zudem würde ich gerne die Sortierung verstehen und wäre sehr froh, wenn mir dennoch jemand genau mit dieser Problematik helfen könnte.
 

framp

Moderator
Teammitglied
hambam schrieb:
Asort habe ich mir auch schon angeschaut, aber das Problem ist, dass ich ja verschiedene Arrays habe und wenn ich ein Array sortiere, verliere ich doch den Bezug zu den anderen.
Der Trick liegt darin asorti zu benutzen. Sieh Dir mal folgenden Code an:

Code:
BEGIN   {
        a["eins"]="1"
        a["zwei"]="2"
        a["drei"]="3"
        a["vier"]="4"
        a["fuenf"]="5"
        a["sechs"]="6"

}
END     {
        n=asorti(a,b)
        for (k in a) {
                print "key: " k " value: " a[k]
        }
        print "-------------"
        for (i=1;i<=n;i++) {
                print i ": " b[i] " - " a[b[i]]
        }

}
 

leanUX

Member
Hi hambam,

hier eine Lösung die mit 2 Skripten und extern aufgerufenem sort arbeitet:

Aufruf mit:
Code:
./script1.awk liste.txt | sort -nr | ./script2.awk


Das Ergebnis mit Deiner Beispielliste sieht dann so aus:
Code:
 Name         Anz        Summe
------------------------------
wesson          3      3000.00
           Nike      2
        Addidas      1

smith           5       550.00
           Puma      3
           Nike      1
        Addidas      1
------------------------------
Gesamt:                3550.00
ich denke so hattest Du Dir das Ergebnis vorgestellt.


script1.awk :
Code:
#!/usr/bin/awk -f

{
        datum = $1
        verkaeufer = $2
        marke = $3
        preis = $4
        gesamt += preis
        tsn[verkaeufer] = verkaeufer
        tsa[verkaeufer] +=1
        tsu[verkaeufer] += preis
        tsmv[verkaeufer,marke] = verkaeufer
        tsma[verkaeufer,marke] += 1
        tsmn[verkaeufer,marke] = marke
}


END {
        print_it(tsa)
}


function print_it(tr)
{
        for (c in tr) {
                if (tsu[c] > 0) {
                        printf "%f %s ZZZZZZZZZZ %d %f\n", tsu[c], tsn[c], tr[c], tsu[c]
                        for (d in tsma) {
                                if (tsn[c] == tsmv[d]) {
                                        printf "%f %s %010d %s %d\n", tsu[c], tsn[c], tsma[d], tsmn[d], tsma[d]
                                }
                        }
                }
        }
}


script2.awk :
Code:
#!/usr/bin/awk -f

BEGIN { letztVerkaeufer = ""
        print ""
        print 
        print " Name         Anz        Summe"
        print "------------------------------"
}


{
        if ($2 != letztVerkaeufer) { 
                if (NR>1) printf "\n"; 
                printf "%-10s %6d %12.2f\n", $2, $4, $5; 
                Summe += $5; 
                letztVerkaeufer = $2
        } else {
                printf "%15s %6d\n", $4, $5
        }
}


END {
        print "------------------------------"
        printf "Gesamt: %22.2f\n\n", Summe
}


Der Trick besteht bei diesem Ansatz darin, zunächst in der Ausgabe von script1.awk die wichtigsten Sortierkriterien am Anfang jeder(!) Zeile (z.T. redundant) auszugeben. Anschließend werden diese Zeilen numerisch absteigend mit sort sortiert. Leider funktioniert das numerische Sortieren hier nur mit der ersten Spalte, daher ist die dritte Spalte mit führenden Nullen versehen, damit auch eine textuelle Sortierung dennoch die richtige Reihenfolge ergibt. Der Verkäufername steht in der 2ten Spalte, damit die Sortierung nach verkauften Schuhen pro Verkäufer auch dann korrekt läuft, wenn zwei Verkäufer den selben Umsatz haben sollten. Die Ausgabe des Strings ZZZZZZZZZZ in script1.awk ist ein dummy-Eintrag für Sortierung nach dieser Spalte.

Ob die Verwendung von gawk hier überhaupt einen vernünftigen Ansatz darstellt weiß ich nicht so recht. Vielleicht wäre es besser, die Daten per einfachem Skript in eine SQlite-Datenbank zu schreiben und auf dieser Basis die Sortierungen etc. vorzunehmen. Also der Import, das Stellen der SQL-Anfrage und das anschließende formatierte Drucken läßt sich leicht in einem Bash-Script durchführen.

Gruß
leanUX
 
A

Anonymous

Gast
ob nun asort() oder asorti() meistens bei Array sind die eingebauten Funktionen so nicht zu verwenden und man muss sich selbst Sortierfunktionen schreiben. Das liegt an der Array Verwendung in awk selbst und an den destruktiven Vorgehen dieser beiden Funktionen.
Helfen könnte dir zB sowas hier.

Code:
function robisort(A,B,C,      t1,t2,i,j,n) {
        delete B
        delete C
        n = 1
        for (i in A){
                B[n] = i
                C[n] = A[i]
                n++
        }
        n--

        for (i = 2; i <= n; ++i)
                for (j = i; C[j-1] > C[j]; --j) {
                        t1 = C[j]
                        t2 = B[j]
                        C[j] = C[j-1]
                        B[j] = B[j-1]
                        C[j-1] = t1
                        B[j-1] = t2
        }
        return n
}

Kurze Erklärung der Verwendung.
Code:
Anzahl = robisort(ARRAY, INDEX-ARRAY, WERT-ARRAY)
Du übergibst ARRAY und bekommst INDEX-ARRAY und WERT-ARRAY zurück. Die korrospondierende Daten aus ARRAY lassen sich dort mit einen INDEX 1,2.3 ...bis Anzahl ansprechen und sind im Index nach Wert sortiert

Beispiel: die 3 größten Werte ausgeben
Code:
BEGIN   {
        a["eins"]="1.23"
        a["zwei"]="1.23"
        a["drei"]="1.5"
        a["vier"]="1.6"
        a["fuenf"]="1.7"
        a["sechs"]="1.08"
}

END     {
        n=robisort(a,b,c)

        for (i=n ; i>n-3 ; i-- ) {
                print n-i+1 ": " b[i] " - " c[i]
        }
}
Bei sehr vielen zu sortieren Werten kannst du den Sortieralgorithmus noch optimieren, ist hier im Beispiel nur ein ganz einfacher verwendet worden.

robi
 

framp

Moderator
Teammitglied
robi schrieb:
ob nun asort() oder asorti() meistens bei Array sind die eingebauten Funktionen so nicht zu verwenden und man muss sich selbst Sortierfunktionen schreiben. Das liegt an der Array Verwendung in awk selbst und an den destruktiven Vorgehen dieser beiden Funktionen.
Helfen könnte dir zB sowas hier.
Warum?
man awk schrieb:
asorti(s [, d]) Returns the number of elements in the source array s. The behavior is the same as that of asort(), except that the array indices are used for sorting, not the array values. When done, the array is indexed numerically, and the values are those of the original indices. The original values are lost; thus provide a second array if you wish to preserve the original.
an den destruktiven Vorgehen
Genau deswegen hat man den zweiten Parameter zur Verfügung um im zweite Array dann die Liste der sortierten Indizes in das erste Array zu bekommen und das erste Array wird nicht verändert.
 
A

Anonymous

Gast
framp schrieb:
mit asort verliere ich den orginal Index, mit asorti kann ich zwar den Index sortieren, verliere aber wiederum den Bezug zu den Werten. Ein "echtes" mehrdimensionales Array geht in AWK nicht. Das ist für indizierte Adressierung wie sie hier verwendet wird natürlich tötlich.

Deine Methode funktioniert zwar, es wird hier das Index sortiert, und damit kann dann über "doppelte Indizierung" auf die Werte des orignal Arrays zugegriffen. In dieser Aufgabe hier soll aber nicht die Verkäufer- oder Artikelnamen sortiert werden, sondern untersucht werden, was die einzelnen Verkäufer/Waren für Werte haben. Dazu müsste das Array erst umgedreht werden, Index wird zu Wert und Wert wird zu Index, was in dieser Aufgabe wiederum völlig inaktzeptabel ist, da der Orginalindex aus Statische- und der Orginalwert aus dynamische Werten aufgebaut ist und spätestes absurt würde, sobald dopptele Werte im Orginalarray vorkommen.

übrigens. asorti ist nicht auch nur ;)
Code:
function asorti(a,b,   c,k,i){
i=1
for (k in a) {
c[i++]=k
}

return asort(c,b)
}
Und damit für indizierte Adressierung wenig bis gar nicht zu gebrauchen.

robi
 

framp

Moderator
Teammitglied
Meine Antwort bezog sich auf 'asort(i)' zerstört das Array - was nicht stimmt wenn ein zweites Array beim Aufruf mitgegeben wird. Dann kann man über die "doppelte Indizierung" sortiert auf die Elemente zugreifen. Also sind wir d'accord.
Ein "echtes" mehrdimensionales Array geht in AWK nicht. Das ist für indizierte Adressierung wie sie hier verwendet wird natürlich tötlich.
Klar, das geht schief da es ja in awk nur eindimensionale Arrays gibt und mehrdimensionale Arrays simuliert werden.
In dieser Aufgabe hier soll aber nicht die Verkäufer- oder Artikelnamen sortiert werdebenutzt.n, sondern untersucht werden, was die einzelnen Verkäufer/Waren für Werte haben. Dazu müsste das Array erst umgedreht werden, Index wird zu Wert und Wert wird zu Index, was in dieser Aufgabe wiederum völlig inaktzeptabel ist, da der Orginalindex aus Statische- und der Orginalwert aus dynamische Werten aufgebaut ist und spätestes absurt würde, sobald dopptele Werte im Orginalarray vorkommen.
Mir ging es bei meiner Frage nur um die 'Unbrauchbarkeit' von asort. Das awk für die Problemstellung ungeeignet ist wurde ja schon ein paar mal erwähnt.
 

abgdf

Guru
Hier mal ein Vorschlag in Python (kann leider kein (g)awk):
Code:
#!/usr/bin/env python
#-*- coding: iso-8859-1 -*-

data = """18.08.2010 smith Nike 100
18.08.2010 smith Addidas 150
18.08.2010 wesson Nike 90
18.08.2010 smith Puma 120
18.08.2010 smith Puma 80
18.08.2010 wesson Nike 110
18.08.2010 wesson Addidas 100
18.08.2001 smith Puma 100"""

data = data.split("\n")

def getJournpart(journ, keynr, key):
    journpart = []
    for i in journ:
        b = i.split(" ")
        if b[keynr] == key:
            journpart.append(i)
    return journpart

def getSorted(journ, keynr):
    items = []
    for i in journ:
        b = i.split(" ")
        if not b[keynr] in items:
            items.append(b[keynr])
    journparts = {}
    for i in items:
        journparts[i] = getJournpart(journ, keynr, i)
    res = []
    for i in items:
        sum = 0
        for u in journparts[i]:
            b = u.split(" ")
            sum += float(b[3])
        res.append((i, sum))
    res.sort(key = lambda t: t[1], reverse = True)
    if len(res) > 5:
        res = res[0:5]
    return res

print "Top 5 Verkäufer:"
for i in getSorted(data, 1):
    print i[0] + "\t\t" + str(i[1])
print 
print "Top 5 Marken:"
for i in getSorted(data, 2):
    print i[0] + "\t\t" + str(i[1])
print 
print "Top 5 Marken der Top 5 Verkäufer:"
verk = getSorted(data, 1)
for i in verk:
    print "Verkäufer: " + i[0]
    for u in getSorted(getJournpart(data, 1, i[0]), 2):
        print u[0] + "\t\t" + str(u[1])
    print
Auch nicht gerade einfach, sollte aber laufen ...

Gruß
 
A

Anonymous

Gast
framp schrieb:
Das awk für die Problemstellung ungeeignet ist wurde ja schon ein paar mal erwähnt.
Ungeeignet ist es nicht, im Gegenteil es ist genau für solche Sachen mal konzipiert worden und mit Sicherheit schneller und resourcenschonender als alles andere. Nur die Funktionen die man braucht die gibt es eben nicht von der Stange, da muss man schon ein bischen Gehinschmalz investieren. Heute macht das kaum noch einer.
[Ironie] Lieber kauft man sich dazu ein bis 3 dicke Server eine Lizenz fürs die Betriebssysteme sowie für SAP und für noch so 10 andere Programme und Tools mehr. Beschäftigt 2 SAP Spezialisten eine Netzwerkmenschen und ein paar PHP- und Webdesigner sowie ganz wichtig hier, eine Projektmanager und hat dann nach einem halben Jahr Projektaufbau mit ein bischen Glück pro Abfrage dann schon nach 1 bis 2 Minuten ein buntes Bild mit der genauen Auswertung von jedem PC aus abrufbar. ;) ;) ;) ;) ;) ;) Und für die Kaffeekasse ist dann auch gleich noch Platz mit in der Datenbank.

Wer beschäftigt sich schon heute noch ernsthaft mit solche alten Tools wie awk und sed, die kann man ja nicht mal mit der Maus bedienen, geschweige denn man kann fertige Libs mit universellen alles erschlagenden Funktionen dazu nutzen, und wie soll man sonst ein Programm entwickeln. Wirklich wegen jeder Aufgabe immer wieder im Urschleim anfangen mit "hello world" ? und jedes Mal ein Problem wirklich immer und jedesmal von Grund auf neu analysieren und selber die Zusammenhänge verstehen? Ne Ne, sowas kann man heut zutage nicht mehr gebrauchen
[/Ironie]


Robi
 

framp

Moderator
Teammitglied
abgdf schrieb:
Hier mal ein Vorschlag in Python (kann leider kein (g)awk):
:D eine Lösung in python habe ich schon erwartet - die ist recht schnell erzeugt :D

awk ist zwar wie robi schon bemerkte alt - aber deshalb nicht untauglich. Nur glaube ich ist das Programmierparadigma von awk nicht mehr en vogue und für Listenverarbeitungen sehr gut geeignet - nur nicht als DBMS Ersatz
 
A

Anonymous

Gast
framp schrieb:
awk ist zwar wie robi schon bemerkte alt - aber deshalb nicht untauglich.
Man muss sich nur mal überlegen es wurde geschrieben für Spezialisten die ihre Programme in Form von Lochkartenstapeln mit weißen Kitteln durch Rechenzentren getragen haben die gewöhnlichen Industiemaschienenhallen glichen. An den Türen hingen "Doktor" und "Proffessor" und von denen konnte wahrscheinlich noch viele 5 bis 10 Sortieralgorithmen in wenigen Minuten auch in einer Programmiersprache und zT auch noch in Assembler selbst programmieren. Für die damalige Benutzer von AWK war es ein Klacks ein Problem soweit zu abstrahieren und logisch in AWK lösbare Einzelschritte zu zerlegen. Wenn die vor 30 Jahren schon gewusst hätten, das man die Hardware mit gesamter Rechenkapazität eines damaligen Rechenzentrums auf 1 Kg schrumpen und zusammenkappbar überall mitnehmen kann, das man darauf ein Unix nicht unähnliches extrem leistungsfähiges Betriebssystem bekommt, und das sich sowas zumindestens in einigen Ländern ein Großteil der Menschen nicht nur finanziell leisten können, sondern es auch nutzten und damit täglich rumrennen, dann hätten die damals AWK bestimmt schon etwas anders programmiert. Trotz alledem, wir haben heute noch Standardmäßig auf jedem Rechner awk installiert, und es funktioniert mit einigen Erweiterungen bis heute immer noch genauso, ist noch genauso leistungsfähig wie damals, wird aber meist nur noch als Einzeiler in anderen Scripten verwendet obwohl es oft genug doch eigentlich die gesamte Arbeit des Scriptes selbst erledigen könnte und es nicht schwieriger ist das in awk anstatt in der Scriptsprache zu programmieren.

robi
 

abgdf

Guru
Ich weiß nicht, mit Perl/Python kann ich mit so wenig Aufwand wie möglich Emails parsen, Zeichnungen plotten, auf Datenbankserver zugreifen, Daten verschlüsseln, grafische Oberflächen bauen, usw.. Und die Datenverarbeitung kriege ich auch noch hin, wobei mir flexible, dynamische Datentypen helfen. Mir scheint, da habe ich einfach mehr Möglichkeiten, wenn ich eines davon lerne, als mit awk.
Aber bitte: awk kann ja auch einiges (mehr als ich dachte) und wenn Du gern darin schreibst, warum nicht?

Peace

abgdf
 
OP
H

hambam

Newbie
Das Script von leanUX funktioniert einwandfrei (auch wenn ich zunächst Zweifel hatte, da bei ihm im Ergebnis ein falscher Wert gezeigt wurde)
Vielen Dank auch den anderen Antwortern, die viel interessantes zum Thema beigetragen haben. Insbesondere Perl/Python scheint auch nicht uninteressant zu sein.
Robi's Irone-Part enthält zudem soviel Wahrheit... :)

Nun schaue ich mal, wie ich nun weitere Statistiken mit diesem Wissen ereugen kann. Zum Beispiel anstatt pro Verkäufer, die Auswertung pro Marke.
Mittelfristig werde ich wohl die Daten in eine DB übertragen, die dann zumindest tagesaktuell sind. Für eine schnelle direkte Auswertung bleibe ich dann wohl bei awk...
 
Oben