Le fichier CSV que je veux lire ne rentre pas dans la mémoire principale. Comment puis-je en lire quelques (~ 10K) lignes aléatoires et faire quelques statistiques simples sur la trame de données sélectionnée?
En supposant qu'aucun en-tête dans le fichier CSV:
import pandas
import random
n = 1000000 #number of records in file
s = 10000 #desired sample size
filename = "data.txt"
skip = sorted(random.sample(xrange(n),n-s))
df = pandas.read_csv(filename, skiprows=skip)
ce serait mieux si read_csv avait un keeprows, ou si skiprows prenait une fonction de rappel au lieu d'une liste.
Avec en-tête et longueur de fichier inconnue:
import pandas
import random
filename = "data.txt"
n = sum(1 for line in open(filename)) - 1 #number of records in file (excludes header)
s = 10000 #desired sample size
skip = sorted(random.sample(xrange(1,n+1),n-s)) #the 0-indexed header will not be included in the skip list
df = pandas.read_csv(filename, skiprows=skip)
@ dlm's answer is great but since v0.20.0, skiprows accepte un callable . L'appelable reçoit en argument le numéro de ligne.
Si vous pouvez spécifier quel pourcentage de lignes vous souhaitez, plutôt que combien de lignes , vous n'avez même pas besoin d'obtenir la taille du fichier et vous n'avez qu'à lire le fichier une fois. En supposant un en-tête sur la première ligne:
import pandas as pd
import random
p = 0.01 # 1% of the lines
# keep the header, then take only 1% of lines
# if random from [0,1] interval is greater than 0.01 the row will be skipped
df = pd.read_csv(
filename,
header=0,
skiprows=lambda i: i>0 and random.random() > p
)
Ou, si vous voulez prendre chaque n
ème ligne:
n = 100 # every 100th line = 1% of the lines
df = pd.read_csv(filename, header=0, skiprows=lambda i: i % n != 0)
Ce n'est pas dans Pandas, mais cela permet d'obtenir le même résultat beaucoup plus rapidement grâce à bash:
shuf -n 100000 data/original.tsv > data/sample.tsv
La commande shuf
va mélanger l'entrée et l'argument et -n
Indique combien de lignes nous voulons dans la sortie.
Question pertinente: https://unix.stackexchange.com/q/108581
Benchmark sur un CSV de 7M de lignes disponible ici (2008):
Meilleure réponse:
def pd_read():
filename = "2008.csv"
n = sum(1 for line in open(filename)) - 1 #number of records in file (excludes header)
s = 100000 #desired sample size
skip = sorted(random.sample(range(1,n+1),n-s)) #the 0-indexed header will not be included in the skip list
df = pandas.read_csv(filename, skiprows=skip)
df.to_csv("temp.csv")
%time pd_read()
CPU times: user 18.4 s, sys: 448 ms, total: 18.9 s
Wall time: 18.9 s
Lors de l'utilisation de shuf
:
time shuf -n 100000 2008.csv > temp.csv
real 0m1.583s
user 0m1.445s
sys 0m0.136s
Ainsi, shuf
est environ 12 fois plus rapide et, surtout, ne lit pas le fichier entier en mémoire.
Voici un algorithme qui ne nécessite pas de compter le nombre de lignes dans le fichier au préalable, vous n'avez donc besoin de lire le fichier qu'une seule fois.
Disons que vous voulez m échantillons. Premièrement, l'algorithme conserve les m premiers échantillons. Lorsqu'il voit le i-ème échantillon (i> m), avec une probabilité m/i, l'algorithme utilise l'échantillon pour remplacer aléatoirement un échantillon déjà sélectionné.
Ce faisant, pour tout i> m, nous avons toujours un sous-ensemble de m échantillons sélectionnés au hasard parmi les premiers i échantillons.
Voir le code ci-dessous:
import random
n_samples = 10
samples = []
for i, line in enumerate(f):
if i < n_samples:
samples.append(line)
Elif random.random() < n_samples * 1. / (i+1):
samples[random.randint(0, n_samples-1)] = line
Le code suivant lit d'abord l'en-tête, puis un échantillon aléatoire sur les autres lignes:
import pandas as pd
import numpy as np
filename = 'hugedatafile.csv'
nlinesfile = 10000000
nlinesrandomsample = 10000
lines2skip = np.random.choice(np.arange(1,nlinesfile+1), (nlinesfile-nlinesrandomsample), replace=False)
df = pd.read_csv(filename, skiprows=lines2skip)
Pas de pandas!
import random
from os import fstat
from sys import exit
f = open('/usr/share/dict/words')
# Number of lines to be read
lines_to_read = 100
# Minimum and maximum bytes that will be randomly skipped
min_bytes_to_skip = 10000
max_bytes_to_skip = 1000000
def is_EOF():
return f.tell() >= fstat(f.fileno()).st_size
# To accumulate the read lines
sampled_lines = []
for n in xrange(lines_to_read):
bytes_to_skip = random.randint(min_bytes_to_skip, max_bytes_to_skip)
f.seek(bytes_to_skip, 1)
# After skipping "bytes_to_skip" bytes, we can stop in the middle of a line
# Skip current entire line
f.readline()
if not is_EOF():
sampled_lines.append(f.readline())
else:
# Go to the begginig of the file ...
f.seek(0, 0)
# ... and skip lines again
f.seek(bytes_to_skip, 1)
# If it has reached the EOF again
if is_EOF():
print "You have skipped more lines than your file has"
print "Reduce the values of:"
print " min_bytes_to_skip"
print " max_bytes_to_skip"
exit(1)
else:
f.readline()
sampled_lines.append(f.readline())
print sampled_lines
Vous vous retrouverez avec une liste sampled_lines. De quel genre de statistiques parlez-vous?
utiliser sous-échantillon
pip install subsample
subsample -n 1000 file.csv > file_1000_sample.csv
class magic_checker:
def __init__(self,target_count):
self.target = target_count
self.count = 0
def __eq__(self,x):
self.count += 1
return self.count >= self.target
min_target=100000
max_target = min_target*2
nlines = randint(100,1000)
seek_target = randint(min_target,max_target)
with open("big.csv") as f:
f.seek(seek_target)
f.readline() #discard this line
Rand_lines = list(iter(lambda:f.readline(),magic_checker(nlines)))
#do something to process the lines you got returned .. perhaps just a split
print Rand_lines
print Rand_lines[0].split(",")
quelque chose comme ça devrait fonctionner, je pense