Mezcle dos archivos de texto paralelos

9

Tengo dos corpus paralelos (archivos de texto) alineados con oraciones con aproximadamente 50 mil palabras. (del corpus Europarl -> traducción paralela de documentos legales). Ahora me gustaría barajar las líneas de los dos archivos, pero ambos de la misma manera. Quería abordar eso usando gshuf (estoy en una Mac) usando una fuente aleatoria única.

gshuf --random-source /path/to/some/random/data file1
gshuf --random-source /path/to/some/random/data file2

Pero recibí el mensaje de error end of file, porque aparentemente la semilla aleatoria debe contener todas las palabras que contiene el archivo a ordenar. ¿Es eso cierto? En caso afirmativo, ¿cómo debo crear una semilla aleatoria que sea buena para mis necesidades? Si no, ¿de qué otra manera podría aleatorizar los archivos en paralelo? Pensé en pegarlos, aleatorizarlos y luego dividirlos nuevamente. Sin embargo, esto parece feo ya que primero necesitaría encontrar un delimitador que no ocurra en los archivos.

conipo
fuente
1
Obtuviste ese error porque tu archivo_aleatorio no contiene suficientes bytes ... Mira random sources. En cuanto a paste, podría usar como delimitador algunos caracteres de baja ascii que es poco probable que ocurran en sus archivos (como \x02, \x03...).
don_crissti
Bien, lo que sea que quiera aleatorizar, si uso / dev / urandom, debería estar listo, ¿no? El delimitador de pasta es un buen consejo, ¡gracias!
conipo

Respuestas:

10

No sé si hay un método más elegante, pero esto funciona para mí:

mkfifo onerandom tworandom threerandom
tee onerandom tworandom threerandom < /dev/urandom > /dev/null &
shuf --random-source=onerandom onefile > onefile.shuf &
shuf --random-source=tworandom twofile > twofile.shuf &
shuf --random-source=threerandom threefile > threefile.shuf &
wait

Resultado:

$ head -n 3 *.shuf
==> onefile.shuf <==
24532 one
47259 one
58678 one

==> threefile.shuf <==
24532 three
47259 three
58678 three

==> twofile.shuf <==
24532 two
47259 two
58678 two

Pero los archivos deben tener exactamente el mismo número de líneas.


La documentación de GNU Coreutils también proporciona una buena solución para la aleatoriedad repetida utilizando opensslcomo generador aleatorio sembrado:

https://www.gnu.org/software/coreutils/manual/html_node/Random-sources.html#Random-sources

get_seeded_random()
{
  seed="$1"
  openssl enc -aes-256-ctr -pass pass:"$seed" -nosalt \
    </dev/zero 2>/dev/null
}

shuf -i1-100 --random-source=<(get_seeded_random 42)

Sin embargo, considere usar una semilla mejor que "42", a menos que quiera que alguien más pueda reproducir "su" resultado aleatorio también.

Frostschutz
fuente
Esto funciona como un encanto. ¿Te importaría explicar los pasos que tomaste? El comando tee asegura que el mismo número aleatorio se almacena en las tres tuberías, ¿verdad? ¿Por qué necesita también salir a / dev / null? ¿Y se asegura automáticamente de que haya suficientes bytes y de que end of fileno se produzca el error?
conipo
El /dev/nulles porque teetambién imprime a stdout. Podría usar > threerandomen su lugar, pero es más difícil de escribir. Las canalizaciones con nombre producirán tantos datos aleatorios como sea necesario, por lo que no tiene que saber de antemano cuánto necesitará.
frostschutz
Ok, ¿y por qué no puede ser una tubería que usas como fuente aleatoria para las 3 barajas una tras otra?
conipo
2
No puede leer los mismos datos tres veces de una tubería. Tienes que multiplexar de alguna manera y eso es lo teeque ...
frostschutz