programing

파일에서 임의의 줄 선택

iphone6s 2023. 5. 22. 20:50
반응형

파일에서 임의의 줄 선택

Bash 스크립트에서는 입력 파일에서 N개의 임의의 행을 선택하여 다른 파일로 출력하고 싶습니다.

이것이 어떻게 행해지는가?

와 함께 사용-n아래에 표시된 것과 같은 옵션을 얻으려면N랜덤 선:

shuf -n N input > output

로 정렬하고 파을임의정렬먼고선저다택니합하로일▁first다▁randomly▁sort▁the니선합택▁file▁pick먼을 선택합니다.100표시:

lines=100
input_file=/usr/share/dict/words

# This is the basic selection method
<$input_file sort -R | head -n $lines

# If the file has duplicates that must never cause duplicate results
<$input_file sort | uniq        | sort -R | head -n $lines

# If the file has blank lines that must be filtered, use sed
<$input_file sed $'/^[ \t]*$/d' | sort -R | head -n $lines

이죠.<$input_file파이프 표준 입력으로 대체할 수 있습니다.이(()sort -R그리고.$'...\t...'갖기 위해sed탭 문자와 일치)는 GNU/리눅스 및 BSD/macOS와 함께 작동합니다.

글쎄요, 쉬프 답변에 대한 댓글에 따르면 그는 1분도 안 되어 78,000,000줄을 쉬프했습니다.

도전 수락...

편집: 제 기록을 깼습니다.

파워슈프는 0.047초 만에 그것을 했습니다.

$ time ./powershuf.py -n 10 --file lines_78000000000.txt > /dev/null 
./powershuf.py -n 10 --file lines_78000000000.txt > /dev/null  0.02s user 0.01s system 80% cpu 0.047 total

왜 이렇게 빠른지, 음, 나는 전체 파일을 읽지 않고 파일 포인터를 10번만 움직이고 포인터 뒤에 줄을 인쇄합니다.

깃랩 레포

오래된 시도

먼저 78.000.000.000 줄의 파일이 필요했습니다.

seq 1 78 | xargs -n 1 -P 16 -I% seq 1 1000 | xargs -n 1 -P 16 -I% echo "" > lines_78000.txt
seq 1 1000 | xargs -n 1 -P 16 -I% cat lines_78000.txt > lines_78000000.txt
seq 1 1000 | xargs -n 1 -P 16 -I% cat lines_78000000.txt > lines_78000000000.txt

이것은 에게 780억 줄의 새로운 줄이 있는 파일을 줍니다 ;-)

이제 쉬프 부분:

$ time shuf -n 10 lines_78000000000.txt










shuf -n 10 lines_78000000000.txt  2171.20s user 22.17s system 99% cpu 36:35.80 total

병목 현상은 CPU였고 여러 스레드를 사용하지 않았으며, 코어 1개를 100% 고정하고 나머지 15개는 사용하지 않았습니다.

Python은 제가 정기적으로 사용하는 것이기 때문에 이것을 더 빠르게 하기 위해 사용할 것입니다.

#!/bin/python3
import random
f = open("lines_78000000000.txt", "rt")
count = 0
while 1:
  buffer = f.read(65536)
  if not buffer: break
  count += buffer.count('\n')

for i in range(10):
  f.readline(random.randint(1, count))

이것은 저를 1분도 채 안되게 했습니다.

$ time ./shuf.py         










./shuf.py  42.57s user 16.19s system 98% cpu 59.752 total

i9와 Samsung NVMe를 탑재한 Lenovo X1 익스트림 2세대에서 이 작업을 수행하여 읽기 및 쓰기 속도가 충분합니다.

더 빨라질 수 있다는 건 알지만 다른 사람들도 시도해 볼 수 있는 공간을 남겨두겠습니다.

라인 카운터 소스: 루터 블리셋

선호하는 옵션은 매우 빠른 것입니다. 13개의 열, 231만 행, 2.0개의 탭으로 구분된 데이터 파일을 샘플링했습니다.GB 압축 해제.

# randomly sample select 5% of lines in file
# including header row, exclude blank lines, new seed

time \
awk 'BEGIN  {srand()} 
     !/^$/  { if (rand() <= .05 || FNR==1) print > "data-sample.txt"}' data.txt

# awk  tsv004  3.76s user 1.46s system 91% cpu 5.716 total
seq 1 100 | python3 -c 'print(__import__("random").choice(__import__("sys").stdin.readlines()))'

Arch의 할 수 있기 에: "Arch"라는 shuffle그러나 줄의 수를 제한하고 맨 페이지에 "셔플이 메모리에 입력을 읽기 때문에 매우 큰 파일에서 오류가 발생할 수 있습니다."라고 경고하는 명령줄 스위치는 없습니다.

# Function to sample N lines randomly from a file
# Parameter $1: Name of the original file
# Parameter $2: N lines to be sampled 
rand_line_sampler() {
    N_t=$(awk '{print $1}' $1 | wc -l) # Number of total lines

    N_t_m_d=$(( $N_t - $2 - 1 )) # Number oftotal lines minus desired number of lines

    N_d_m_1=$(( $2 - 1)) # Number of desired lines minus 1

    # vector to have the 0 (fail) with size of N_t_m_d 
    echo '0' > vector_0.temp
    for i in $(seq 1 1 $N_t_m_d); do
            echo "0" >> vector_0.temp
    done

    # vector to have the 1 (success) with size of desired number of lines
    echo '1' > vector_1.temp
    for i in $(seq 1 1 $N_d_m_1); do
            echo "1" >> vector_1.temp
    done

    cat vector_1.temp vector_0.temp | shuf > rand_vector.temp

    paste -d" " rand_vector.temp $1 |
    awk '$1 != 0 {$1=""; print}' |
    sed 's/^ *//' > sampled_file.txt # file with the sampled lines

    rm vector_0.temp vector_1.temp rand_vector.temp
}

rand_line_sampler "parameter_1" "parameter_2"

아래의 'c'는 입력에서 선택할 라인의 수입니다.필요에 따라 수정:

#!/bin/sh

gawk '
BEGIN   { srand(); c = 5 }
c/NR >= rand() { lines[x++ % c] = $0 }
END { for (i in lines)  print lines[i] }

' "$@"

언급URL : https://stackoverflow.com/questions/9245638/select-random-lines-from-a-file

반응형