Zählen Sie die Zeilen, die das Wort enthalten

7

Ich habe eine Datei mit mehreren Zeilen. Ich möchte für jedes Wort, das in der Gesamtdatei erscheint, wissen, wie viele Zeilen dieses Wort enthalten, zum Beispiel:

0 hello world the man is world
1 this is the world
2 a different man is the possible one

Das erwartete Ergebnis ist:

0:1
1:1
2:1
a:1
different:1
hello:1
is:3
man:2
one:1
possible:1
the:3
this:1
world:2

Beachten Sie, dass die Anzahl für "Welt" 2 und nicht 3 beträgt, da das Wort in 2 Zeilen erscheint. Aus diesem Grund wäre die Übersetzung von Leerzeichen in Zeilenumbrüche nicht die exakte Lösung.

Netzsooc
quelle
Was hast du im Moment versucht?
Romeo Ninov
Dies scheint sehr relevant zu sein: unix.stackexchange.com/a/332890/224077
Panki

Antworten:

5

Eine weitere Perl-Variante mit List :: Util

$ perl -MList::Util=uniq -alne '
  map { $h{$_}++ } uniq @F }{ for $k (sort keys %h) {print "$k: $h{$k}"}
' file
0: 1
1: 1
2: 1
a: 1
different: 1
hello: 1
is: 3
man: 2
one: 1
possible: 1
the: 3
this: 1
world: 2
Steeldriver
quelle
5

Straightfoward-ish in Bash:

declare -A wordcount
while read -ra words; do 
    # unique words on this line
    declare -A uniq
    for word in "${words[@]}"; do 
        uniq[$word]=1
    done
    # accumulate the words
    for word in "${!uniq[@]}"; do 
        ((wordcount[$word]++))
    done
    unset uniq
done < file

Betrachten Sie die Daten:

$ declare -p wordcount
declare -A wordcount='([possible]="1" [one]="1" [different]="1" [this]="1" [a]="1" [hello]="1" [world]="2" [man]="2" [0]="1" [1]="1" [2]="1" [is]="3" [the]="3" )'

und Formatierung nach Ihren Wünschen:

$ printf "%s\n" "${!wordcount[@]}" | sort | while read key; do echo "$key:${wordcount[$key]}"; done
0:1
1:1
2:1
a:1
different:1
hello:1
is:3
man:2
one:1
possible:1
the:3
this:1
world:2
Glenn Jackman
quelle
4

Es ist ein ziemlich einfaches Perl-Skript:

#!/usr/bin/perl -w
use strict;

my %words = ();
while (<>) {
  chomp;
  my %linewords = ();
  map { $linewords{$_}=1 } split / /;
  foreach my $word (keys %linewords) {
    $words{$word}++;
  }
}

foreach my $word (sort keys %words) {
  print "$word:$words{$word}\n";
}

Die Grundidee besteht darin, die Eingabe zu durchlaufen. Teilen Sie sie für jede Zeile in Wörter auf und speichern Sie diese Wörter in einem Hash (assoziatives Array), um alle Duplikate zu entfernen. Führen Sie dann eine Schleife über dieses Array von Wörtern und fügen Sie eines zu einem Gesamtzähler für dieses Wort hinzu. Berichten Sie am Ende über die Wörter und ihre Anzahl.

Jeff Schaller
quelle
1
Ein kleines Problem dabei ist meiner Meinung nach, dass es die übliche Definition eines Wortes nicht respektiert, da es sich in ein einzelnes Leerzeichen aufteilt. Wenn irgendwo zwei Leerzeichen gefunden würden, würde eine leere Zeichenfolge dazwischen ebenfalls als Wort betrachtet, wenn ich mich nicht irre. Geschweige denn, wenn Wörter durch andere Satzzeichen getrennt wurden. Natürlich wurde in der Frage nicht angegeben, ob "Wort" als das Konzept des Programmierers eines "Wortes" oder als ein Wort einer natürlichen Sprache verstanden wird.
Larry
2

Eine Lösung, die mehrere Programme von einer Shell aus aufruft:

fmt -1 words.txt | sort -u | xargs -Ipattern sh -c 'echo "pattern:$(grep -cw pattern words.txt)"'

Eine kleine Erklärung:

Das fmt -1 words.txtdruckt alle Wörter aus, 1 pro Zeile, und | sort -usortiert diese Ausgabe und extrahiert nur die eindeutigen Wörter daraus.

Um das Vorkommen eines Wortes in einer Datei zu zählen, kann man grep(ein Werkzeug zum Durchsuchen von Dateien nach Mustern) verwenden. Durch Übergeben der -cwOption gibt grep die Anzahl der gefundenen Wortübereinstimmungen an. So können Sie die Gesamtzahl der patternVerwendungsvorfälle ermitteln grep -cw pattern words.txt.

Das Tool xargsermöglicht es uns, dies für jedes einzelne Wort zu tun, das von ausgegeben wird sort. Dies -Ipatternbedeutet, dass der folgende Befehl mehrmals ausgeführt wird, wobei jedes Vorkommen eines Musters durch ein Wort ersetzt wird, das von der Standardeingabe gelesen wird sort.

Die Indirektion mit shist erforderlich, da xargsnur ein einzelnes Programm unter seinem Namen ausgeführt werden kann und alles andere als Argumente übergeben wird. xargsbehandelt keine Dinge wie die Ersetzung von Befehlen. Der $(...)Befehl is substitution im obigen Snippet ersetzt die Ausgabe von grepin echound ermöglicht die korrekte Formatierung. Da wir die Befehlsersetzung benötigen, müssen wir den sh -cBefehl verwenden, der alles, was er erhält, als Argument in seiner eigenen Shell ausführt.

Larry
quelle
Eine Optimierung für diesen Ansatz:fmt -1 words.txt | sort | uniq -c | awk '{ print $2 ":" $1 }'
Matja
@matja ist sort | uniq -ceffizienter als sort -u?
Vikarjramun
vikarjramun @ no, aber uniq -c gibt Ihnen die Anzahl jedes Wortes in einem Durchgang an, sodass Sie nicht xargs verwenden müssen, um mehrere Durchgänge der Eingabedatei für jedes Wort durchzuführen.
Matja
1
@matja: Ich habe tatsächlich die Antwort gegeben, die Sie vor der aktuellen gegeben haben. Es macht jedoch nicht das, was OP verlangt hat. Ich habe die Frage zunächst auch völlig falsch verstanden und wurde von Glenn Jackman korrigiert. Was Sie vorschlagen, würde jedes Vorkommen jedes Wortes zählen. Was OP verlangt, ist, die Anzahl der Zeilen zu zählen, in denen jedes Wort mindestens einmal vorkommt.
Larry
2

Eine andere einfache Alternative wäre die Verwendung von Python (> 3.6). Diese Lösung hat das gleiche Problem wie die von @Larry in seinem Kommentar erwähnte .

from collections import Counter

with open("words.txt") as f:
    c = Counter(word for line in [line.strip().split() for line in f] for word in set(line))
    for word, occurrence in sorted(c.items()):
        print(f'{word}:{occurrence}')
        # for Python 2.7.x compatibility you can replace the above line with 
        # the following one:
        # print('{}:{}'.format(word, occurrence))

Eine explizitere Version der obigen Version:

from collections import Counter


FILENAME = "words.txt"


def find_unique_words():
    with open(FILENAME) as f:
        lines = [line.strip().split() for line in f]

    unique_words = Counter(word for line in lines for word in set(line))
    return sorted(unique_words.items())


def print_unique_words():
    unique_words = find_unique_words()
    for word, occurrence in unique_words:
        print(f'{word}:{occurrence}')


def main():
    print_unique_words()


if __name__ == '__main__':
    main()

Ausgabe:

0:1
1:1
2:1
a:1
different:1
hello:1
is:3
man:2
one:1
possible:1
the:3
this:1
world:2

Das obige setzt auch voraus, dass sich words.txt im selben Verzeichnis wie script.py befindet . Beachten Sie, dass sich dies nicht wesentlich von anderen hier angebotenen Lösungen unterscheidet, aber vielleicht wird es jemand nützlich finden.

Grajdeanu Alex.
quelle
0

Ich versuche es mit awk zu machen:

count.awk :

#!/usr/bin/awk -f
# count line containing word

{
    for (i = 1 ; i <= NF ; i++) {
        word_in_a_line[$i] ++
        if (word_in_a_line[$i] == 1) {
            word_line_count[$i] ++
        }
    }

    delete word_in_a_line
}

END {
    for (word in word_line_count){
        printf "%s:%d\n",word,word_line_count[word]
    }
}

Führen Sie es aus:

$ awk -f count.awk ./test.data | sort
Charles
quelle
0

Eine reine Bash-Antwort

echo "0 hello world the man is world
1 this is the world
2 a different man is the possible one" | while IFS=$'\n' read -r line; do echo $line | tr ' ' '\n' | sort -u; done | sort | uniq -c


   1 0
   1 1
   1 2
   1 a
   1 different
   1 hello
   3 is
   2 man
   1 one
   1 possible
   3 the
   1 this
   2 world

Ich habe in jeder Zeile eindeutige Wörter wiederholt und an übergeben uniq -c

edit: ich habe glenns antwort nicht gesehen. Ich fand es seltsam, keine Bash-Antwort zu sehen

user1462442
quelle
0

Einfach, aber egal, ob die Datei oft gelesen wird:

sed 's/ /\n/g' file.txt | sort | uniq | while read -r word; do
  printf "%s:%d\n" "$word" "$(grep -Fw "$word" file.txt | wc -l)"
done

BEARBEITEN: Trotz der Konvertierung von Leerzeichen in Zeilenumbrüche werden Zeilen gezählt, bei denen jedes Wort vorkommt und nicht die Vorkommen der Wörter selbst. Es gibt das Ergebnis:

0:1
1:1
2:1
a:1
different:1
hello:1
is:3
man:2
one:1
possible:1
the:3
this:1
world:2

Dies ist zeichenweise identisch mit dem Beispielergebnis von OP.

JoL
quelle
1
Lesen Sie die Frage noch einmal. Es heißt wörtlich translating blanks to newline chars wouldn't be the exact solution.
Sparhawk
@Sparhawk Lesen Sie die Antwort noch einmal. Dies gibt die Antwort, die er als Beispiel gegeben hat, einschließlich der Angabe des Ergebnisses von 2 anstelle von 3 für die Welt. Er meinte, dass so etwas sed 's/ /\n/g' | sort | uniq -cnicht funktionieren würde, weil es die Antwort 3 für die Welt geben würde, aber das ist nicht das, was diese Antwort tut. Es zählt die Zeilen, in denen die Wörter vorkommen, und nicht die Vorkommen selbst korrekt, genau wie es OP wollte.
JoL
Ah richtig, entschuldigung! Ich würde empfehlen, eine Erklärung Ihres Codes einzugeben, die sowohl für den Fragesteller hilfreich ist als auch klarstellt, was er tut. Als kleinen Punkt möchten Sie wahrscheinlich auch read -rhier.
Sparhawk