2011-10-24 7 views
6

Lo que me gustaría hacer es mezclar las filas (leer de CSV), luego imprimir las primeras 10.000 filas aleatorias a una csv y el resto a una csv por separado. Con un archivo más pequeño que puedo hacer algo comoEn Java, ¿hay alguna manera de aleatorizar un archivo demasiado grande para que quepa en la memoria?

java.util.Collections.shuffle(...) 
for (int i=0; i < 10000; i++) printcsv(...) 
for (int i=10000; i < data.length; i++) printcsv(...) 

Sin embargo, con archivos muy grandes ahora consigo OutOfMemoryError

+0

Podría mapear la memoria del archivo y leer partes del archivo. – Thomas

+3

Parece que necesita más memoria. :-) – nfechner

+0

@Thomas No creo que ese sea el problema. El póster necesita mantener todas las entradas en la memoria, si quiere aleatorizarlas antes de escribirlas en el archivo. – nfechner

Respuesta

1

Aquí es posible un algoritmo:

  1. Let MAX_LINES sea el número máximo de líneas en un archivo manejable;
  2. Lea MAX_LINES del archivo de entrada, aleatorícelos con su algoritmo original y escríbalos en un archivo temporal;
  3. Repita 2. hasta que no queden líneas en su archivo de entrada;
  4. Deje que N sea un número aleatorio entre 0 y la cantidad de archivos temporales que escribió; lea la siguiente línea del N-ésimo archivo temporal;
  5. Repita 4. hasta que lea todas las líneas de todos los archivos; las primeras 10.000 veces que escriba cada línea en el primer archivo de salida, escriba todas las demás líneas en el otro archivo.
+0

Veo un problema con el paso 4 si MAX_LINES << TOTAL_LINES. No puede mantener todos los archivos abiertos a la vez y leerá un total (aproximado) de MAX_LINES * TOTAL_LINES/2 (para cada línea que se debe generar se debe leer un archivo que contenga MAX_LINES, pero en promedio solo la mitad). El >> factor en eso que la oportunidad de leer el mismo archivo para la próxima N es muy pequeña. (si MAX_LINES = TOTAL_LINES, la probabilidad sería 100%) – Stefan

+0

p = MAX_LINES/TOTAL_LINES. La posibilidad de leer la siguiente línea del mismo archivo es p. De lo contrario, debe leer un archivo nuevo y omitir la mitad de las filas. ((1-p) * MAX_LINES/2 + p * 1) es el esfuerzo por línea procesada => ((1-p) * MAX_LINES/2 + p * 1) * TOTAL_LINES = esfuerzo total. Para p << 1 da la fórmula dada anteriormente que conduce a un resultado 'extraño'. Es mínimo para MAX_LINES = 1. Pero la creación de algunos millones de archivos puede no ser una opción tampoco. – Stefan

+0

Lo siento, se dejó llevar ... – Stefan

3

Usted podría:

  • uso más memoria o

  • no aleatoria las filas de CSV reales, pero solo una colección de números de fila, y luego leer el archivo de entrada línea por línea (en búfer, por supuesto) y escribir la línea en una de las líneas archivos de salida conectados.

+2

Este enfoque preservaría el orden original. –

+0

Buen punto. Pensé que la tarea era solo elegir una muestra aleatoria de todas las filas. Sin embargo, si su suposición es correcta, es posible que se produzca un remezclado posterior en la memoria del archivo de salida, ya que es mucho más pequeño que el archivo original. – michael667

+0

Hay 2 archivos de salida y si es correcto, tiene el mismo problema con el segundo archivo si el número de líneas que puede manejar en la memoria es mucho menor que el número total de líneas. – Stefan

1

Utilice algún tipo de esquema de indexación. Analice su archivo CSV una vez para obtener el número de filas (no retenga nada en la memoria, simplemente analice) y elija 10,000 números de ese rango al azar (asegúrese de evitar duplicados, por ejemplo con un Set<Integer> o algo más sofisticado)) Luego analice su CSV por segunda vez, manteniendo una vez más un contador para las filas. Si un número de fila corresponde a uno de sus números elegidos al azar, déjelo en un archivo CSV. Imprima las filas con un número que no coincida con el otro archivo.

+2

Esto aún conservaría el orden original. –

+0

@NicolaMusatti: Vaya, tienes razón. Olvidé ese requisito. En ese caso, diría que es mejor con una simple tabla de base de datos para guardar temporalmente los datos. Tal vez JavaDB para mantener las cosas simples ... –

1
  1. Antes que nada, cuente el número de líneas en el archivo de entrada leyendo sus contenidos (pero no almacenándolos en la memoria). Llame al número de líneas N.
  2. Take a random sample del tamaño 10,000 de los números 1 .. N.
  3. Lea el archivo de entrada desde el principio. Para cada línea, si el número de línea está en la muestra extraída en el paso 2, escriba la línea en file1; de lo contrario, escríbalo al file2.

Paso 2 se puede lograr mientras de realizar el paso 1 mediante el uso de reservoir sampling.

+1

También conservas el orden original –

0

Si conoce el número de líneas en su archivo y si está aleatorizando filas completas, puede aleatorizar por número de línea y luego leer esa fila seleccionada. Simplemente seleccione una línea al azar a través de la clase Random y almacene la lista de números aleatorios, para que no elija uno dos veces.

BufferedReader reader = new BufferedReader(new FileReader(new File("file.cvs"))); 
BufferedWriter chosen = new BufferedWriter(new FileWriter(new File("chosen.cvs"))); 
BufferedWriter notChosen = new BufferedWriter(new FileWriter(new File("notChosen.cvs"))); 

int numChosenRows = 10000; 
long numLines = 1000000000; 

Set<Long> chosenRows = new HashSet<Long>(numChosenRows+1, 1); 
for(int i = 0; i < numChosenRows; i++) { 
    while(!chosenRows.add(nextLong(numLines))) { 
     // add returns false if the value already exists in the Set 
    } 
} 

String line; 
for(long lineNo = 0; (line = reader.readLine()) != null; lineNo++){ 
    if(chosenRows.contains(lineNo)){ 
     // Do nothing for the moment 
    } else { 
     notChosen.write(line); 
    } 
} 

// Randomise the set of chosen rows 

// Use RandomAccessFile to write the rows in that order 

Ver this answer para el método nextLong, que produce una aleatoria larga reducido a un tamaño particular.

Editar: Como la mayoría de las personas, pasé por alto el requisito de escribir las líneas seleccionadas al azar en un orden aleatorio. Supongo que RandomAccessFile me ayudaría con eso. Simplemente aleatorice la Lista con las filas elegidas y acceda a ellas en ese orden. En cuanto a los no elegidos, edité el código anterior para simplemente ignorar los elegidos.

+0

RandomAccessFile y 'líneas' no van bien entre sí. Debería crear también una asignación de filas para iniciar el índice final. – Stefan

2

Puede mapear la memoria del archivo y encontrar todas las líneas nuevas, almacenar en una matriz de int o long donde se encuentran. Cree una matriz de índices int y revíselos. Esto debería usar aproximadamente 8-32 bytes por línea. Si esto no cabe en la memoria, también puede usar archivos mapeados en memoria para estas matrices.

Cuestiones relacionadas