
Ich habe über 5000 durch Leerzeichen getrennte Zeilen wie unten:
Item_A: Acou#1 Bla#5
Item_B: Acou#1 Elfa#2 Flq#2
Item_C: Acou#1 Bla#4 Elfa#2 Flq#2
Item_D: Agly#3 Bla#4 Elfa#2
Ich möchte eine Tabelle mit gemeinsamen Überschriften für alle und jeweils einer Menge in einer Tabelle wie unten erstellen,
Acou Agly Bla Elfa Flq
Item_A: 1 0 5 0 0
Item_B: 1 0 0 2 2
Item_C: 1 0 4 2 2
Item_D: 0 3 4 2 0
Ich habe Zeilen mit „Acou“, dann „Bla“ usw. durchsucht. Anschließend habe ich sie in Excel bearbeitet, um sie zu quantifizieren und alle einzelnen Dateien zu einer Datei zusammenzufügen. Das hat jedoch viel Zeit in Anspruch genommen.
Antwort1
BEGIN { OFS = "\t" }
# Collect headers from data
FNR == NR {
for (i = 2; i <= NF; ++i)
if (!($i in heads))
heads[$i]
next
}
# Output header
FNR == 1 {
line = "Items"
for (j in heads)
line = line OFS j
print line
}
{
line = $1
# Iterate through the header items, testing each field against it
for (j in heads) {
found = 0 # assume not found
for (i = 2; !found && i <= NF; ++i)
if ($i == j)
found = 1 # matches header
line = line OFS found
}
print line
}
Ausführen des folgenden Vorgangs auf Ihren Daten (nachdem Sie leere Zeilen entfernt haben):
$ awk -f script.awk file file
Items Acou#1 Bla#4 Bla#5 Elfa#2 Agly#3 Flq#2
Item_A: 1 0 1 0 0 0
Item_B: 1 0 0 1 0 1
Item_C: 1 1 0 1 0 1
Item_D: 0 1 0 1 1 0
Beachten Sie, dass Sie die Eingabedatendatei zweimal angeben müssen. Dies liegt daran, dass wir sie zweimal scannen. Beim ersten Scan sammeln wir die Datenelemente in jeder Zeile (dem FNR == NR
Block). Beim zweiten Scan testen wir jedes gesammelte Datenelement (die Header) anhand der Daten in jeder Zeile.
Die Ausgabe erfolgt einfach, 0
wenn das Feld im Header in den Daten in dieser Zeile nicht vorhanden ist und 1
wenn es vorhanden ist. Dies ist nichtganzwas du verlangt hast, also …
Eine Variante, die die Überschriften am abschneidet und den Teil nach dem als anzuzeigende Daten #
verwendet :#
BEGIN { OFS = "\t" }
# Collect headers from data
FNR == NR {
for (i = 2; i <= NF; ++i) {
split($i, h, "#")
if (!(h[1] in heads))
heads[h[1]]
}
next
}
# Output header
FNR == 1 {
line = "Items"
for (j in heads)
line = line OFS j
print line
}
{
line = $1
# Iterate through the header items, testing each field against it
for (j in heads) {
found = 0 # assume not found
for (i = 2; !found && i <= NF; ++i) {
split($i, h, "#")
if (h[1] == j)
found = h[2] # matches header
}
line = line OFS found
}
print line
}
Ausführen:
$ awk -f script.awk file file
Items Elfa Bla Acou Agly Flq
Item_A: 0 5 1 0 0
Item_B: 2 0 1 0 2
Item_C: 2 4 1 0 2
Item_D: 2 4 0 3 0
Beachten Sie, dass die Reihenfolge der Spalten nicht unbedingt sortiert ist (da sie als Schlüssel in einem assoziativen Array gespeichert sind). Ich überlasse es dem Leser, sie zu sortieren.
Antwort2
Wenn es Ihnen nichts ausmacht,GNU Datamashin die Mischung, dann könnten Sie die Einträge einfach serialisieren und sie dann in einer Kreuztabelle darstellen:
awk '
{for (i=2;i<=NF;i++) {split($i,a,"#"); print $1,a[1],a[2]}}' OFS='\t' file |
datamash --filler=0 crosstab 1,2 count 3
Acou Agly Bla Elfa Flq
Item_A: 1 0 1 0 0
Item_B: 1 0 0 1 1
Item_C: 1 0 1 1 1
Item_D: 0 1 1 1 0
Alternativ mit GNU awk (das mehrdimensionale Arrays zulässt):
gawk '
BEGIN {
OFS="\t";
PROCINFO["sorted_in"] = "@ind_str_asc";
}
{
for (i=2;i<=NF;i++) {
split($i,a,"#");
h[a[1]] = 1;
t[$1][a[1]] += a[2];
}
}
END {
for (j in h) printf("\t%s", j);
printf "\n";
for (i in t) {
printf("%s",i);
for (j in h)
printf("\t%d", j in t[i] ? t[i][j] : 0);
printf "\n";
}
}' file
Acou Agly Bla Elfa Flq
Item_A: 1 0 5 0 0
Item_B: 1 0 0 2 2
Item_C: 1 0 4 2 2
Item_D: 0 3 4 2 0