L'autre jour, 100 Language Processing Knock 2020 a été publié. Je ne travaille moi-même sur le langage naturel que depuis un an, et je ne connais pas les détails, mais je vais résoudre tous les problèmes et les publier afin d'améliorer mes compétences techniques.
Tout doit être exécuté sur le notebook jupyter, et les restrictions de l'énoncé du problème peuvent être facilement rompues. Le code source est également sur github. Oui.
Le chapitre 1 est ici.
L'environnement est Python 3.8.2 et Ubuntu 18.04.
Je pense que ici est plus facile à comprendre en tant qu'article de commentaire. J'aimerais que les auteurs rédigent des articles de commentaire jusqu'au chapitre 10.
popular-names.txt est un fichier qui stocke le «nom», le «sexe», le «nombre de personnes» et «l'année» d'un bébé né aux États-Unis dans un format délimité par des tabulations. Créez un programme qui effectue le traitement suivant et exécutez popular-names.txt en tant que fichier d'entrée. De plus, exécutez le même processus avec une commande UNIX et vérifiez le résultat de l'exécution du programme.
Veuillez télécharger l'ensemble de données requis depuis ici.
Le fichier téléchargé est placé sous «données».
Comptez le nombre de lignes. Utilisez la commande wc pour confirmation.
code
with open('data/popular-names.txt') as f:
print(len(list(f)))
production
2780
Recherchez simplement la longueur de l'objet fichier. Étant donné que l'objet fichier est un itérateur, il doit être répertorié. Si l'entrée est un fichier suffisamment volumineux, il peut ne pas tenir dans la mémoire, mais dans ce cas, vous pouvez simplement le tourner avec l'instruction for et le compter.
code
wc -l < data/popular-names.txt
production
2780
Trouvez le nombre de lignes en spécifiant l'option l dans la commande wc
. Si vous donnez un nom de fichier, divers extras seront affichés, alors donnez-le à partir de l'entrée standard.
Remplacez chaque onglet par un espace. Utilisez la commande sed, la commande tr ou la commande expand pour confirmation.
code
with open('data/popular-names.txt') as f:
for line in f:
line = line.strip()
line = line.replace('\t', ' ')
print(line)
production(10 premières lignes)
Mary F 7065 1880
Anna F 2604 1880
Emma F 2003 1880
Elizabeth F 1939 1880
Minnie F 1746 1880
Margaret F 1578 1880
Ida F 1472 1880
Alice F 1414 1880
Bertha F 1320 1880
Sarah F 1288 1880
Chaque chaîne obtenue en transformant l'objet fichier en itérateur a un caractère de saut de ligne à la fin, donc supprimez-la avec strip
(rstrip ('\ n')
peut être préférable).
Remplacez simplement les onglets par des espaces et une sortie.
Il existe également une méthode de print (line, end = '')
sans supprimer le caractère de saut de ligne avec strip
.
code
awk '{gsub("\t", " ", $0); print $0}' data/popular-names.txt
perl -pe 's/\t/ /g' data/popular-names.txt
sed 's/\t/ /g' data/popular-names.txt
expand -t 1 data/popular-names.txt
tr '\t' ' ' < data/popular-names.txt
La sortie est la même que celle de Python, je vais donc l'omettre. (Il en va de même par la suite)
Je ne me souviens pas de nombreuses commandes UNIX.
Enregistrez la colonne extraite de chaque ligne sous col1.txt et la colonne extraite 2 sous col2.txt. Utilisez la commande cut pour confirmation.
code
with open('data/popular-names.txt') as f, \
open('result/col1.txt', 'w') as g, \
open('result/col2.txt', 'w') as h:
for line in f:
line = line.strip()
pref, city, _, _ = line.split('\t')
print(pref, file=g)
print(city, file=h)
Je l'ai écrit docilement.
résultat(col1.10 premières lignes de txt)
Mary
Anna
Emma
Elizabeth
Minnie
Margaret
Ida
Alice
Bertha
Sarah
résultat(col2.10 premières lignes de txt)
F
F
F
F
F
F
F
F
F
F
code
cut -f 1 data/popular-names.txt > col1.txt
cut -f 2 data/popular-names.txt > col2.txt
C'est facile avec la commande cut
. ʻAwk '{print $ 1}' `est bien.
Combinez les col1.txt et col2.txt créés dans 12 pour créer un fichier texte dans lequel les première et deuxième colonnes du fichier d'origine sont disposées en tabulation. Utilisez la commande coller pour confirmation.
Tout ce que vous avez à faire est d'ouvrir les deux fichiers séparément, mais comme c'est un gros problème, utilisez contextlib.ExitStack
pour l'implémenter afin de pouvoir gérer n'importe quel nombre de fichiers.
Pour le gestionnaire de contexte → https://docs.python.org/ja/3/library/stdtypes.html#typecontextmanager
code
from contextlib import ExitStack
code
files = ['result/col1.txt', 'result/col2.txt']
with ExitStack() as stack:
files = [stack.enter_context(open(filename)) for filename in files]
for lines in zip(*files):
x = [line.strip() for line in lines]
x = '\t'.join(x)
print(x)
résultat(10 premières lignes)
Mary F
Anna F
Emma F
Elizabeth F
Minnie F
Margaret F
Ida F
Alice F
Bertha F
Sarah F
code
paste result/col1.txt result/col2.txt
C'est facile avec la commande coller.
Recevoir le nombre naturel N au moyen d'un argument de ligne de commande et afficher uniquement les N premières lignes de l'entrée. Utilisez la commande head pour confirmation.
Personnellement, j'aime recevoir des arguments d'entrée et de ligne de commande standard avec ʻargparseet
fileinput`, mais cette fois je veux pouvoir exécuter tout le code sur le notebook jupyter, donc j'utilise des arguments de ligne de commande ne pas. (Je ressens de la gentillesse envers "etc." dans l'énoncé du problème)
code
N = 5
with open('data/popular-names.txt') as f:
lst = range(N)
for _, line in zip(lst, f):
print(line, end='')
production
Mary F 7065 1880
Anna F 2604 1880
Emma F 2003 1880
Elizabeth F 1939 1880
Minnie F 1746 1880
Vous pouvez faire de même avec la commande head
.
code
head -n 5 data/popular-names.txt
Recevoir le nombre naturel N au moyen d'un argument de ligne de commande et n'afficher que les N dernières lignes de l'entrée. Utilisez la commande tail pour confirmation.
Je pense que c'est correct de mettre toutes les entrées standard dans la liste et d'extraire les 5 derniers éléments, mais si c'est un gros fichier, il peut ne pas tenir dans la mémoire, donc j'utiliserai la file d'attente.
code
from collections import deque
code
N = 5
queue = deque([], 5)
with open('data/popular-names.txt') as f:
for line in f:
queue.append(line)
for line in queue:
print(line, end='')
production
Benjamin M 13381 2018
Elijah M 12886 2018
Lucas M 12585 2018
Mason M 12435 2018
Logan M 12352 2018
Vous pouvez faire la même chose avec la commande tail
.
code
tail -n 5 data/popular-names.txt
Recevez le nombre naturel N au moyen d'arguments de ligne de commande et divisez le fichier d'entrée en N ligne par ligne. Réalisez le même traitement avec la commande de fractionnement.
Je pense que c'est parce qu'il n'y a pas beaucoup de situations où un fichier est divisé en N lignes par ligne, mais en fonction de l'implémentation de la commande de fractionnement, il peut ne pas y avoir N divisions par ligne. Ce peut être une extension GNU.
code(5 divisions)
split -d -nl/5 data/popular-names.txt result/shell5.
résultat(Vérifiez le nombre de lignes avec wc)
587 2348 11007 result/shell5.00
554 2216 11010 result/shell5.01
556 2224 11006 result/shell5.02
540 2160 11007 result/shell5.03
543 2172 10996 result/shell5.04
2780 11120 55026 total
Je l'ai également implémenté en python afin qu'il se comporte de la même manière que le code de cette extension GNU (https://github.com/coreutils/coreutils/blob/master/src/split.c).
code
def split_string_list(N, lst):
chunk_size = sum([len(x) for x in lst]) // N
chunk_ends = [chunk_size * (n + 1) - 1 for n in range(N)]
i = 0
acc = 0
out = []
for chunk_end in chunk_ends:
tmp = []
while acc < chunk_end:
tmp.append(lst[i])
acc += len(lst[i])
i += 1
out.append(tmp)
return out
def split_file(N, filepath, outprefix):
with open(filepath) as f:
lst = list(f)
lst = split_string_list(N, lst)
for i, lines in enumerate(lst):
idx = str(i).zfill(2) #Omission
with open(outprefix + idx, 'w') as f:
f.write(''.join(lines))
split_file(5, 'data/popular-names.txt', 'result/python5.')
Tout d'abord, comptez le nombre total de caractères et décidez de la position de coupe (chunk_ends
) afin que le nombre de caractères soit aussi uniforme que possible.
Ensuite, il prend une ligne jusqu'à ce qu'il dépasse chaque élément de chunk_ends, et quand il dépasse, il le sort dans un fichier.
résultat(Vérifiez le nombre de lignes avec wc)
587 2348 11007 result/python5.00
554 2216 11010 result/python5.01
556 2224 11006 result/python5.02
540 2160 11007 result/python5.03
543 2172 10996 result/python5.04
2780 11120 55026 total
résultat
diff result/python5.00 result/shell5.00
diff result/python5.01 result/shell5.01
diff result/python5.02 result/shell5.02
diff result/python5.03 result/shell5.03
diff result/python5.04 result/shell5.04
J'ai obtenu le même résultat.
Recherchez le type de chaîne de caractères dans la première colonne (un ensemble de chaînes de caractères différentes). Utilisez les commandes cut, sort et uniq pour confirmation.
code
names = set()
with open('data/popular-names.txt') as f:
for line in f:
name = line.split('\t')[0]
names.add(name)
names = sorted(names)
for name in names:
print(name)
résultat(10 premières lignes)
Abigail
Aiden
Alexander
Alexis
Alice
Amanda
Amelia
Amy
Andrew
Angela
La première colonne est ajoutée à l'ensemble dans l'ordre, triée et sortie. (* La version de python est 3.8.2.)
code
cut -f 1 data/popular-names.txt | sort -s | uniq
Prenez uniquement la première colonne et triez pour supprimer les doublons. Si vous oubliez d'entrer le tri, ce sera étrange. De plus, l'option s est ajoutée pour assurer un tri stable selon python.
Disposez chaque ligne dans l'ordre inverse des nombres de la troisième colonne (Remarque: réorganisez le contenu de chaque ligne sans les changer) Utilisez la commande sort pour confirmation (ce problème ne doit pas nécessairement correspondre au résultat de l'exécution de la commande).
code
with open('data/popular-names.txt') as f:
lst = [line.strip() for line in f]
lst.sort(key = lambda x : -int(x.split('\t')[2]))
for line in lst[:10]:
print(line)
production(10 premières lignes)
Linda F 99689 1947
Linda F 96211 1948
James M 94757 1947
Michael M 92704 1957
Robert M 91640 1947
Linda F 91016 1949
Michael M 90656 1956
Michael M 90517 1958
James M 88584 1948
Michael M 88528 1954
En spécifiant la clé de la fonction sort
, vous pouvez spécifier les critères de tri.
code
sort -nrsk 3 data/popular-names.txt
Vous ne pouvez le faire qu'avec la commande sort
. C'est facile.
Trouvez la fréquence d'apparition de la première colonne de chaque ligne et affichez-les par ordre décroissant. Utilisez les commandes cut, uniq et sort pour confirmation.
Utilisez collections.Counter
.
code
from collections import Counter
code
cnt = Counter()
with open('data/popular-names.txt') as f:
for line in f:
name = line.split('\t')[0]
cnt.update([name])
lst = cnt.most_common()
lst.sort(key=lambda x:(-x[1], x[0]))
for name, num in lst[:10]:
print(name)
production
James
William
John
Robert
Mary
Charles
Michael
Elizabeth
Joseph
Margaret
Soit passez la liste telle quelle à l'objet de Counter
, soit passez-la petit à petit avec ʻupdate ().
most_common ()` les organisera par ordre décroissant.
code
cut -f 1 data/popular-names.txt | sort | uniq -c | sort -nrsk1 | awk '{print $2}'
Si vous ajoutez l'option -c
lorsque vous prenez ʻuniq`, il comptera combien il y en a. Enfin, triez par numéro pour obtenir le résultat souhaité.
Traitement du langage 100 coups 2020 Chapitre 3: Expressions régulières
Recommended Posts