데이터 파일에서 특정 수의 선을 임의로 그립니다. 데이터 파일에서

나는 데이터 목록을 가지고있다.

12345
23456
67891
-20000
200
600
20
...

이 데이터 세트의 크기 (즉, 파일 라인)가이라고 가정하십시오 N. m이 데이터 파일에서 무작위로 선을 그리고 싶습니다 . 따라서 출력은 두 개의 파일이어야합니다. 하나는 이러한 m데이터 라인을 포함하는 파일 이고 다른 하나는 N-m데이터 라인을 포함 합니다.

Linux 명령을 사용하여이를 수행 할 수있는 방법이 있습니까?



답변

이것은 가장 효율적인 방법은 아니지만 작동합니다.

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

$m라인의 수를 포함.


답변

이 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

질문의 데이터를 기반으로하는 출력입니다.

12345
23456
200
600
========
67891
-20000
20

답변

모든 유닉스와 마찬가지로 해당 TM에 대한 유틸리티가 있습니다 .

오늘의 프로그램 : 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구현에는 -R플래그 가 없다는 것이 주목되었습니다 . 당신이 가진 경우에 perl, 당신은 대신 할 수 있습니다 perl -e 'use List::Util qw/shuffle/; print shuffle <>;'.


답변

줄 순서를 바꾸지 않아도되고 GNU coreutils가있는 경우 (즉, shuf버전 6.0에 등장한 이후로 너무 오래되지 않은 내장되지 않은 Linux 또는 Cygwin )shuf (“셔플”)은 파일의 줄을 무작위로 다시 정렬합니다. 따라서 파일을 섞어서 첫 번째 m 행을 한 파일로, 나머지 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는 난수 생성기를 초기화하는 데별로 좋지 않습니다. 무작위성은 암호화에 적합하지 않을뿐만 아니라 수치 시뮬레이션에도 적합하지 않습니다. 시드는 1 초의 기간이있는 시스템에서 모든 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

답변

가정 m = 7N = 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}변수 확장을하지 않는 표기법을.

파일에서 줄 단위로 삭제하여 짧아지고 짧아 지므로 제거 할 수있는 줄 번호가 점점 작아야합니다.

이것은 더 긴 파일과 많은 행에 사용해서는 안됩니다. 모든 수에 대해 평균적으로 첫 번째 파일의 절반 파일과 두 번째 sed 코드 의 전체 파일을 읽어야하기 때문 입니다.