Случайным образом потяните определенное число строк из файла данных

По-моему, один из лучших ресурсов для LVM был Красными Шляпами Руководство администратора LVM, проверьте его.

13
22.01.2012, 15:49
5 ответов

Это не могло бы быть самым эффективным путем, но он работает:

shuf <file> > tmp
head -n $m tmp > out1
tail -n +$(( m + 1 )) tmp > out2

С $m содержа количество строк.

18
27.01.2020, 19:52
  • 1
    @userunknown, sort -R заботится о случайности. Не уверенный, если Вы downvoted ответ для этого, но ищут его в странице справочника сначала. –  Rob Wouters 22.01.2012, 16:31
  • 2
    Отметьте это sort -R точно не сортирует его вход случайным образом: это группирует идентичные строки. Таким образом, если вход, например. foo, foo, bar, bar и m=2, затем один файл будет содержать обоих foos и другой будет содержать обоих bars. GNU coreutils также имеет shuf, который рандомизирует входные строки. Кроме того, Вам не нужен временный файл. –  Gilles 'SO- stop being evil' 23.01.2012, 02:45
  • 3
    , почему нет shuf <file> |head -n $m? –  emanuele 19.06.2014, 19:56
  • 4
    @emanuele: Поскольку нам нужны и голова и в хвост в двух отдельных файлах. –  Rob Wouters 20.06.2014, 10:39

Этот bash/awk сценарий выбирает строки наугад и поддерживает исходную последовательность в обоих выходных файлах.

awk -v m=4 -v N=$(wc -l <file) -v out1=/tmp/out1 -v out2=/tmp/out2 \
 'BEGIN{ srand()
         do{ lnb = 1 + int(rand()*N)
             if ( !(lnb in R) ) {
                 R[lnb] = 1
                 ct++ }
         } while (ct<m)
  } { if (R[NR]==1) print > out1 
      else          print > out2       
  }' file
cat /tmp/out1
echo ========
cat /tmp/out2

Вывод, базирующийся ont данные в вопросе.

12345
23456
200
600
========
67891
-20000
20
5
27.01.2020, 19:52

Как со всеми вещами Unix, существует Утилита для ThatTM.

Программа дня: split
split разделит файл по-разному, -b байты, -l строки, -n количество выходных файлов. Мы будем использовать -l опция. Так как Вы хотите выбрать случайные строки и не просто первое m, мы будем sort файл случайным образом сначала. Если Вы хотите читать о sort, обратитесь к моему ответу здесь.

Теперь, фактический код. Это довольно просто, действительно:

sort -R input_file | split -l $m output_prefix

Это сделает два файла, один с m строки и один с N-m строки, названные output_prefixaa и output_prefixab. Удостовериться m больший файл, который Вы хотите, или Вы получите несколько файлов длины m (и один с N % m).

Если Вы хотите удостовериться, чтобы Вы использовали корректный размер, вот немного кода, чтобы сделать это:

m=10 # size you want one file to be
N=$(wc -l input_file)
m=$(( m > N/2 ? m : N - m ))
sort -R input_file | split -l $m output_prefix

Править: Это привлекло мое внимание что некоторые sort реализации не имеют a -R флаг. Если Вы имеете perl, можно занять место perl -e 'use List::Util qw/shuffle/; print shuffle <>;'.

4
27.01.2020, 19:52
  • 1
    К сожалению, sort -R кажется, только находится в некоторых версиях вида (вероятно, версия гну). Для других платформ я записал инструмент, названный 'randline', который действительно только рандомизирует stdin. Это по beesbuzz.biz/code для любого, кому нужен он. (Я склонен переставлять содержание файла довольно много.) –  fluffy 22.01.2012, 20:49
  • 2
    Отметьте это sort -R точно не сортирует его вход случайным образом: это группирует идентичные строки. Таким образом, если вход, например. foo, foo, bar, bar и m=2, затем один файл будет содержать обоих foos и другой будет содержать обоих bars. GNU coreutils также имеет shuf, который рандомизирует входные строки. Кроме того, можно выбрать имена выходного файла при помощи head и tail вместо split. –  Gilles 'SO- stop being evil' 23.01.2012, 02:48

Если Вы не возражаете переупорядочивать строки, и у Вас есть GNU coreutils (т.е. на невстроенном Linux или Cygwin, не слишком древнем с тех пор shuf появившийся в версии 6.0), shuf (“перестановка”) переупорядочивает строки файла случайным образом. Таким образом, можно переставить файл и диспетчеризировать первые m строки в один файл и остальных в другого.

Нет никакого идеального способа сделать ту отправку. Вы не можете просто объединить в цепочку head и tail потому что head буферизовал бы вперед. Можно использовать split, но Вы не получаете гибкости относительно имен выходного файла. Можно использовать awk, конечно:

<input shuf | awk -v m=$m '{ if (NR <= m) {print >"output1"} else {print} }'

Можно использовать sed, который неясен, но возможно быстрее для больших файлов.

<input shuf | sed -e "1,${m} w output1" -e "1,${m} d" >output2

Или можно использовать tee копировать данные, если Ваша платформа имеет /dev/fd; это в порядке, если m является маленьким:

<input shuf | { tee /dev/fd/3 | head -n $m >output1; } 3>&1 | tail -n +$(($m+1)) >output2

Портативно, можно использовать awk для диспетчеризации каждой строки в свою очередь. Обратите внимание, что awk не очень хорош при инициализации его генератора случайных чисел; случайность только определенно не не подходит для криптографии, но и даже очень хороша для численного моделирования. Семя будет тем же для всех awk вызовов на любом системном скручивании жгутов один второй период.

<input awk -v N=$(wc -l <input) -v m=3 '
    BEGIN {srand()}
    {
        if (rand() * N < m) {--m; print >"output1"} else {print >"output2"}
        --N;
    }'

При необходимости в лучшей случайности можно сделать то же самое в Perl, который отбирает его RNG прилично.

<input perl -e '
    open OUT1, ">", "output1" or die $!;
    open OUT2, ">", "output2" or die $!;
    my $N = `wc -l <input`;
    my $m = $ARGV[0];
    while (<STDIN>) {
        if (rand($N) < $m) { --$m; print OUT1 $_; } else { print OUT2 $_; }
        --$N;
    }
    close OUT1 or die $!;
    close OUT2 or die $!;
' 42
4
27.01.2020, 19:52
  • 1
    @Gilles предположения: Для awk пример: -v N=$(wc -l <file) -v m=4 ... и это только печатает "случайную" строку, когда случайное значение является меньше, чем $m, вместо печати $m случайные строки... Это кажется этим perl может делать то же самое с рэндом, но я не знаю perl достаточно хорошо закончить ошибку компиляции: синтаксическая ошибка в-e строке 7, рядом"), печатают" –  Peter.O 23.01.2012, 06:49
  • 2
    @Peter. O Спасибо, это - то, что прибывает из ввода в браузере и небрежно редактирования. Я исправил код жемчуга и awk. –  Gilles 'SO- stop being evil' 23.01.2012, 12:12
  • 3
    Все 3 метода, работающие хорошо и быстро.. спасибо (+1)... Я медленно получаю голову вокруг жемчуга..., и это - особенно интересное и полезное разделение файла в shuf пример. –  Peter.O 24.01.2012, 01:03
  • 4
    buffereing проблема?. Я пропускаю что-то? head cat комбинация вызывает потерю данных в следующем втором тесте 3-4.... ТЕСТ 1-2 { for i in {00001..10000} ;do echo $i; done; } | { head -n 5000 >out1; cat >out2; } .. ТЕСТ 3-4 { for i in {00001..10000} ;do echo $i; done; } >input; cat input | { head -n 5000 >out3; cat >out4; } ... wc -l результаты для выводов ТЕСТА 1-2 5000 5 000 (пользы), но для ТЕСТА 3-4 5000 4539 (не хорош).. differnece варьируется в зависимости от включенных размеров файла... Вот ссылка на мой тестовый код –  Peter.O 24.01.2012, 06:00
  • 5
    @Peter. O Право снова, спасибо. Действительно, head чтения вперед; то, что это читает вперед и не распечатывает, отбрасывается. Я обновил свой ответ с менее изящным, но (я довольно уверен), правильные решения. –  Gilles 'SO- stop being evil' 24.01.2012, 17:10

Принятие m = 7 и N = 21:

cp ints ints.bak
for i in {1..7}
do
    rnd=$((RANDOM%(21-i)+1))
    # echo $rnd;  
    sed -n "${rnd}{p,q}" 10k.dat >> mlines 
    sed -i "${rnd}d" ints 
done

Примечание: Если Вы заменяете 7 с переменной как $1 или $m, необходимо использовать seq, не {from..to}- нотация, которая не делает переменного расширения.

Это работает путем удаления линию за линией из файла, который становится короче и короче, таким образом, номер строки, который может быть удален, должен стать меньшим и меньшим.

Это не должно использоваться для более длинных файлов и многих строк, так как для каждого числа, в среднем, половина файла должна быть считана из 1-го, и целый файл для 2-го кода sed.

2
27.01.2020, 19:52
  • 1
    Ему нужен файл со строками, которые удалены также. –  Rob Wouters 22.01.2012, 16:36
  • 2
    , я думал "включая эти m строки данных", должен означать including them но исходные строки также - поэтому including, нет consisting of, и не использование only, но я предполагаю, что Ваша интерпретация, что означал user288609. Я скорректирую свой сценарий соответственно. –  user unknown 22.01.2012, 16:39
  • 3
    Хорошие Взгляды''''. –  Rob Wouters 22.01.2012, 16:52
  • 4
    @user: Вы имеете +1 в неправильном месте. Это должно быть rnd=$((RANDOM%(N-i)+1)) где N=21 в Вашем примере.. Это в настоящее время вызывает sed отказать когда rnd оценен к 0... Кроме того, это не масштабируется очень хорошо со всей той перезаписью файла. например, 123 секунды для извлечения 5 000 случайных строк из 10 000 файлов строки по сравнению с 0,03 секундами для более прямого метода... –  Peter.O 23.01.2012, 14:04
  • 5
    @Peter. O: Вы правы (исправленный), и Вы правы. –  user unknown 23.01.2012, 14:38

Теги

Похожие вопросы