Lire des lignes aléatoires à partir d'un énorme fichier CSV en Python
j'ai ce très gros fichier CSV (15 Go) et j'ai besoin de lire environ 1 million de lignes au hasard. Pour autant que je puisse voir - et implémenter - L'utilitaire CSV en Python ne permet d'itérer que de manière séquentielle dans le fichier.
c'est très exigeant en mémoire de lire le fichier entier en mémoire d'utiliser un choix aléatoire et c'est très long d'aller à travers tout le fichier et de rejeter certaines valeurs et en choisir d'autres, donc, est-il de toute façon choisir une ligne aléatoire à partir du fichier CSV et lecture seule cette ligne?
j'ai essayé sans succès:
import csv
with open('linear_e_LAN2A_F_0_435keV.csv') as file:
reader = csv.reader(file)
print reader[someRandomInteger]
Un exemple de fichier CSV:
331.093,329.735
251.188,249.994
374.468,373.782
295.643,295.159
83.9058,0
380.709,116.221
352.238,351.891
183.809,182.615
257.277,201.302
61.4598,40.7106
10 réponses
import random
filesize = 1500 #size of the really big file
offset = random.randrange(filesize)
f = open('really_big_file')
f.seek(offset) #go to random position
f.readline() # discard - bound to be partial line
random_line = f.readline() # bingo!
# extra to handle last/first line edge cases
if len(random_line) == 0: # we have hit the end
f.seek(0)
random_line = f.readline() # so we'll grab the first line instead
comme @AndreBoos l'a souligné, cette approche entraînera une sélection biaisée. Si vous connaissez la longueur minimale et maximale de la ligne, vous pouvez supprimer ce biais en faisant ce qui suit:
supposons (dans ce cas) que nous ayons min = 3 et max=15
1) Trouvez la longueur (Lp) de la ligne précédente.
alors si Lp = 3, la ligne est la plus défavorable. Donc nous devrions le prendre 100% du temps Si Lp = 15, la ligne est la plus biaisée. Nous ne devrions prendre que 20% de la le choix de l'heure est 5* plus probable.
nous accomplissons ceci en gardant au hasard la ligne X% du temps où:
X = min / Lp
si nous ne gardons pas la ligne, nous faisons un autre choix aléatoire jusqu'à ce que notre lancer de dés soit bon. : -)
j'ai ce très gros fichier CSV (15 Go) et j'ai besoin de lire environ 1 million de lignes aléatoires à partir de celui-ci
en supposant que vous n'avez pas besoin exactement 1 million de lignes et connaissez donc le nombre de lignes dans votre fichier CSV à l'avance, vous pouvez utiliser échantillonnage du réservoir pour récupérer votre sous-ensemble aléatoire. Itérez simplement vos données et déterminez pour chaque ligne les chances que la ligne soit sélectionnée. De cette façon, vous n'avez besoin que d'une seule passe de votre données.
cela fonctionne bien si vous avez besoin d'extraire les échantillons aléatoires souvent, mais l'ensemble de données réel change rarement (puisque vous aurez seulement besoin de garder une trace du nombre d'entrées chaque fois que l'ensemble de données change).
chances_selected = desired_num_results / total_entries
for line in csv.reader(file):
if random() < chances_selected:
result.append(line)
vous pouvez utiliser une variation du méthode probabiliste pour le choix aléatoire d'une ligne dans un fichier.
au lieu de simplement garder un nombre simple qui est choisi, vous pouvez garder un tampon de taille C
. Pour chaque numéro de ligne, n
dans le fichier N
lignes, vous voulez choisir la ligne avec une probabilité C/n
(plutôt que l'original 1/n
. Si le nombre est sélectionné, vous choisissez alors un emplacement aléatoire dans le tampon C-length expulser.
Voici comment cela fonctionne:
import random
C = 2
fpath = 'somelines.txt'
buffer = []
f = open(fpath, 'r')
for line_num, line in enumerate(f):
n = line_num + 1.0
r = random.random()
if n <= C:
buffer.append(line.strip())
elif r < C/n:
loc = random.randint(0, C-1)
buffer[loc] = line.strip()
ceci nécessite un passer par le fichier (temps linéaire) et renvoie exactementC
les lignes du fichier. Chaque ligne aura probabilité C/N
d'être sélectionné.
pour vérifier que les travaux ci-dessus, j'ai créé un fichier avec 5 lignes contenant a,b,C,D,E. J'ai lancé le code 10 000 fois avec C=2. Cela devrait produire environ une distribution égale des 5 choisir 2 (ainsi 10) choix possibles. Les résultats:
a,b: 1046
b,c: 1018
b,e: 1014
a,c: 1003
c,d: 1002
d,e: 1000
c,e: 993
a,e: 992
a,d: 985
b,d: 947
si vous voulez saisir des lignes aléatoires de nombreuses fois (par exemple, mini-lots pour l'apprentissage automatique), et que vous n'avez pas d'objection à scanner l'énorme fichier une fois (sans le charger en mémoire), alors vous pouvez créer une liste d'indeces de ligne et utiliser seek pour saisir rapidement les lignes (basé sur la réponse de Maria Zverina).
# Overhead:
# Read the line locations into memory once. (If the lines are long,
# this should take substantially less memory than the file itself.)
fname = 'big_file'
s = [0]
linelocs = [s.append(s[0]+len(n)) or s.pop(0) for n in open(fname)]
f = open(fname) # Reopen the file.
# Each subsequent iteration uses only the code below:
# Grab a 1,000,000 line sample
# I sorted these because I assume the seeks are faster that way.
chosen = sorted(random.sample(linelocs, 1000000))
sampleLines = []
for offset in chosen:
f.seek(offset)
sampleLines.append(f.readline())
# Now we can randomize if need be.
random.shuffle(sampleLines)
Si les lignes sont vraiment .format csv et pas de champ fixe, alors non, il n'y en a pas. Vous pouvez parcourir le fichier une fois, en indexant les offsets byte pour chaque ligne, puis quand plus tard nécessaire n'utilisez que l'ensemble d'index, mais il n'y a aucun moyen de prédire a priori l'emplacement exact du caractère \n de fin de ligne pour les fichiers CSV arbitraires.
une autre solution est possible si vous connaissez le nombre total de lignes-générer 1 million de nombres aléatoires (random.sample(xrange(n), 1000000)
) jusqu'à le nombre total de lignes comme un ensemble, puis utilisez:
for i, line in enumerate(csvfile):
if i in lines_to_grab:
yield line
cela vous donnera exactement 1 million de lignes d'une manière impartiale, mais vous devez avoir le nombre de lignes à l'avance.
si vous pouvez placer ces données dans une base de données sqlite3, sélectionner un certain nombre de lignes aléatoires est trivial. Vous n'aurez pas besoin de pré-lire ou pad lignes dans le fichier. Puisque les fichiers de données de sqlite sont binaires, votre fichier de données sera de 1/3 à 1/2 plus petit que le texte CSV.
Vous pouvez utiliser un script comme importer le fichier CSV ou, mieux encore, il suffit d'écrire vos données dans une table de base de données en premier lieu. SQLITE3 fait partie de la distribution Python.
puis utilisez ces instructions pour obtenir 1.000.000 de lignes aléatoires:
mydb='csv.db'
con=sqlite3.connect(mydb)
with con:
cur=con.cursor()
cur.execute("SELECT * FROM csv ORDER BY RANDOM() LIMIT 1000000;")
for row in cur.fetchall():
# now you have random rows...
vous pouvez réécrire le fichier avec des enregistrements de longueur fixe, puis effectuer un accès aléatoire sur le fichier Intermédiaire plus tard:
ifile = file.open("inputfile.csv")
ofile = file.open("intermediatefile.csv",'w')
for line in ifile:
ofile.write(line.rstrip('\n').ljust(15)+'\n')
Ensuite, vous pouvez faire:
import random
ifile = file.open("intermediatefile.csv")
lines = []
samples = random.sample(range(nlines))
for sample in samples:
ifile.seek(sample)
lines.append(ifile.readline())
nécessite plus d'espace disque, et le premier programme peut prendre un certain temps à exécuter, mais il permet un accès aléatoire illimité plus tard aux enregistrements avec le second.
# pass 1, count the number of rows in the file
rowcount = sum(1 for line in file)
# pass 2, select random lines
file.seek(0)
remaining = 1000000
for row in csv.reader(file):
if random.randrange(rowcount) < remaining:
print row
remaining -= 1
rowcount -= 1
Dans cette méthode, on génère un nombre aléatoire ensemble, dont le nombre d'éléments est égal au nombre de lignes à lire, avec sa gamme étant le nombre de lignes présentes dans les données. Il est ensuite trié du plus petit au plus grand et stockées.
Ensuite le fichier csv est lu ligne par ligne et un line_counter
est en place pour indiquer le numéro de ligne. Ce line_counter
est alors vérifié avec le premier élément de la liste des nombres aléatoires triés et s'ils sont identiques, alors cette ligne spécifique est écrite en le nouveau fichier csv et le premier élément est supprimé de la liste et le déjà second élément prend la place du premier et le cycle continue.
import random
k=random.sample(xrange(No_of_rows_in_data),No_of_lines_to_be_read)
Num=sorted(k)
line_counter = 0
with open(input_file,'rb') as file_handle:
reader = csv.reader(file_handle)
with open(output_file,'wb') as outfile:
a=csv.writer(outfile)
for line in reader:
line_counter += 1
if line_counter == Num[0]:
a.writerow(line)
Num.remove(Num[0])
if len(Num)==0:
break