ejemplos - Lea una pequeña muestra aleatoria de un gran archivo CSV en un marco de datos de Python
django (8)
El archivo CSV que quiero leer no cabe en la memoria principal. ¿Cómo puedo leer algunas (~ 10K) líneas aleatorias y hacer algunas estadísticas simples en el marco de datos seleccionado?
¡Sin 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
Terminará con una lista de sampled_lines. ¿Qué tipo de estadísticas quieres decir?
Aquí hay un algoritmo que no requiere contar el número de líneas en el archivo de antemano, por lo que solo necesita leer el archivo una vez.
Digamos que quieres m muestras. Primero, el algoritmo mantiene las primeras m muestras. Cuando ve la i-ésima muestra (i> m), con probabilidad m / i, el algoritmo usa la muestra para reemplazar aleatoriamente una muestra ya seleccionada.
Al hacerlo, para cualquier i> m, siempre tenemos un subconjunto de m muestras seleccionadas al azar de las primeras i muestras.
Vea el código a continuación:
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
El siguiente código lee primero el encabezado y luego una muestra aleatoria en las otras líneas:
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)
Esto no está en Pandas, pero logra el mismo resultado mucho más rápido a través de bash:
shuf -n 100000 data/original.tsv > data/sample.tsv
El comando shuf
mezclará la entrada y el argumento y -n
indica cuántas líneas queremos en la salida.
Pregunta relevante: https://unix.stackexchange.com/q/108581
La answer de @dlm es genial, pero desde v0.20.0, skiprows acepta un invocable . El invocable recibe como argumento el número de fila.
Si puede especificar qué porcentaje de líneas desea, en lugar de cuántas líneas , ni siquiera necesita obtener el tamaño del archivo y solo necesita leer el archivo una vez. Suponiendo un encabezado en la primera fila:
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
)
O bien, si quiere tomar cada n
ésima línea:
n = 100 # every 100th line = 1% of the lines
df = pd.read_csv(filename, header=0, skiprows=lambda i: i % n != 0)
Suponiendo que no hay un encabezado en el archivo 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)
Sería mejor si read_csv tuviera un keeprows, o si skiprows tomara un func de devolución de llamada en lugar de una lista.
Con encabezado y longitud de archivo desconocida:
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)
usar subsample
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(",")
algo así debería funcionar, creo