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


13

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

12345
23456
67891
-20000
200
600
20
...

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

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


1
일련의 라인이 걱정됩니까? 예. 소스 순서를 유지하고 싶습니까, 아니면 그 순서 자체가 무작위이고 임의의 행을 선택하고 싶습니까?
Peter.O

답변:


18

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

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

$m라인의 수를 포함.


@userunknown sort -R은 임의성을 처리합니다. 당신이 그것에 대한 답을 하향 조정했는지 확실하지 않지만 맨 페이지에서 먼저 찾으십시오.
Rob Wouters

2
sort -R를 그룹에게 동일한 라인 : 정확히 무작위로 입력을 분류하지 않습니다. 입력이 그렇다면 예컨대 foo, foo, bar, bar이고, m = 2 인 경우, 하나 개의 파일을 모두 포함 할 것이다 foo들과 다른 두 포함될 bar들. GNU coreutils에는 또한 shuf입력 라인을 무작위 화하는가 있습니다. 또한 임시 파일이 필요하지 않습니다 .
Gilles 'SO- 악마 그만'

왜 안돼 shuf <file> |head -n $m?
emanuele

@ emanuele : 두 개의 별도 파일에 머리와 꼬리가 모두 필요하기 때문입니다.
Rob Wouters 2014 년

5

이 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

4

모든 유닉스와 마찬가지로 해당 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 <>;'.


1
불행히도 sort -R일부 버전 (아마도 gnu 버전)에만있는 것으로 보입니다. 다른 플랫폼의 경우 stdin을 무작위로 지정하는 'randline'이라는 도구를 작성했습니다. 그것은에서의 beesbuzz.biz/code 그것을 필요로 누군가를 위해. (파일 내용을 많이
솜털

1
sort -R를 그룹에게 동일한 라인 : 정확히 무작위로 입력을 분류하지 않습니다. 입력이 그렇다면 예컨대 foo, foo, bar, bar이고, m = 2 인 경우, 하나 개의 파일을 모두 포함 할 것이다 foo들과 다른 두 포함될 bar들. GNU coreutils에는 또한 shuf입력 라인을 무작위 화하는가 있습니다. 또한 대신에 를 사용하여 출력 파일 이름을 선택할 수 있습니다headtailsplit .
Gilles 'SO- 악마 그만'

4

줄 순서를 바꾸지 않아도되고 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

@Gilles : 에 대한 awk예 : -v N=$(wc -l <file) -v m=4... 그리고 그것은 단지 인쇄 "랜덤" 임의의 값보다 작 때 라인 $m이 아니라 인쇄보다, $m임의의 줄을 ... 그 것 perl과 같은 일을 수행 할 수있다 랜드를 ,하지만 난 돈 perl컴파일 오류를
지나칠

@ Peter.O 감사합니다. 브라우저에 입력하고 부주의하게 편집 한 결과입니다. awk 및 perl 코드를 수정했습니다.
Gilles 'SO- 악마 그만'

세 가지 방법 모두 잘 작동하고 빠릅니다. 감사합니다. (+1) ... 나는 천천히 perl 주위에 머리를 싣고 있습니다 ... 그리고 그것은 shuf예제 에서 특히 흥미롭고 유용한 파일 분할입니다 .
Peter.O

버퍼링 문제? . 뭔가 빠졌습니까? head cat콤보 번째 테스트는 다음의 데이터의 손실을 야기 3-4 .... TEST 1-2 { for i in {00001..10000} ;do echo $i; done; } | { head -n 5000 >out1; cat >out2; } .. TEST 3-4 { for i in {00001..10000} ;do echo $i; done; } >input; cat input | { head -n 5000 >out3; cat >out4; } ... wc -l의 출력에 대한 결과 TEST 1-2 이다 5000 5000 (양호)이지만위한 TEST 3-45000 4539입니다 (좋지 않음).
차이점

@ Peter.O 다시 한번 감사합니다. 실제로, head미리 읽는다; 미리 읽고 인쇄하지 않은 내용은 버립니다. 나는 대답을 덜 우아하지만 (합리적으로 확신합니다) 올바른 해결책으로 업데이트했습니다.
Gilles 'SO- 악마 그만'

2

가정 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 코드 의 전체 파일을 읽어야하기 때문 입니다.


그는 제거 된 행이있는 파일이 필요합니다.
Rob Wouters

내 말은해야 "이러한 데이터 m 라인을 포함한"생각 including them때문에 - 그러나뿐만 아니라 원래의 선 including이 아니라 consisting of사용하지 only,하지만 난 당신의 해석은 의미 user288609 무엇을 것 같다. 그에 따라 스크립트를 조정하겠습니다.
사용자가 알 수 없음

좋아 보인다 ````
Rob Wouters

@user unknown : +1잘못된 위치에 있습니다. rnd=$((RANDOM%(N-i)+1))예제에서 N = 21 이되어야합니다 . 현재 로 평가 sed될 때 충돌이 발생 rnd합니다 0. .. 또한 모든 파일을 다시 쓸 때 확장 성이 떨어집니다. 예를 들어, 123 초 10,000 선 파일에서 5,000 무작위 라인 추출하는 0.03 초 ... 더 직접적인 방법에 대해
Peter.O

@ Peter.O : 당신이 맞고 (수정되었습니다) 당신이 맞습니다.
사용자가 알 수 없음
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.