web-dev-qa-db-fra.com

Lire un petit échantillon aléatoire d'un gros fichier CSV dans une trame de données Python

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?

46
P.Escondido

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)
53
dlm

@ 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)
28
exp1orer

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.

15
Bar

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
10
desktable

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)
2
queise

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?

1
Vagner Guedes

utiliser sous-échantillon

pip install subsample
subsample -n 1000 file.csv > file_1000_sample.csv
1
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

0
Joran Beasley