Phương pháp tốt nhất để thu thập mẫu ngẫu nhiên từ bộ sưu tập tệp


23

Giả sử có một thư mục chứa 300 tệp dữ liệu. Tôi muốn chọn ngẫu nhiên 200 trong số các tệp đó và di chuyển chúng vào một thư mục khác. Có cách nào để làm điều đó trong Unix / Linux không?


R có lẽ có thể làm điều này trong nháy mắt với list.files()...
sr_

4
Tôi mơ hồ cắm lại với nhau shufhead(hoặc chỉ sử dụng shuf -n, nên đọc trang nam ...)
Ulrich Schwarz

Câu trả lời:


32

Nếu hệ thống của bạn có shuf, bạn có thể sử dụng điều này khá thuận tiện (thậm chí xử lý tên tệp xấu xí):

shuf -zen200 source/* | xargs -0 mv -t dest

Nếu bạn không có shufnhưng phải sort-R, điều này sẽ hoạt động:

find source -type f -print0 | sort -Rz | cut -d $'\0' -f-200 | xargs -0 mv -t dest

7
À đúng rồi, bởi vì nơi nào khác người ta sẽ tìm kiếm sự xáo trộn hơn là trong một công cụ để sắp xếp. (Ít nhất shuflà không được gọi trosvì nó trái ngược với cách sắp xếp.)
Ulrich Schwarz

2
Không có thứ gì trái ngược với cách sắp xếp (theo nghĩa tương tự như không có thứ gọi là "không có thời tiết"). Ngẫu nhiên vẫn được sắp xếp, nó chỉ được sắp xếp ngẫu nhiên.
Plutor

1
"-Zen200" là gì? Điều đó không có trong bất kỳ tài liệu nào cho shuf, hoặc bất cứ nơi nào trên Internet, nhưng ví dụ của bạn không hoạt động mà không có nó. Khá thần bí.
SigmaX

2
@SigmaX Thật vậy, khá zen, không phải vậy. Gợi ý: đó là 3 lá cờ riêng biệt.
Kevin

2
files=(*)
for (( i=0; i<200; i++ )); do
    keys=("${!files[@]}")
    rnd=$(( RANDOM % ${#keys[@]} ))
    key=${keys[$rnd]}
    mv "${files[$key]}" "$otherdir"
    unset files[$key]
done

2

Đặt tất cả tên tệp vào một mảng có tên "files" trong bash:

files=( * )

kích thước của mảng:

echo ${#files[@]}

xác định 2/3 trong số chúng là cỡ mẫu:

take=$((2*${#files[@]}/3)) 

for i in $(seq 1 $take)
do
    r=$((RANDOM%${#files[@]})) 
    echo ${files[r]}
done

Điều này sẽ chọn bản sao, và đang không thử nghiệm với tên tập tin bằng các đoạn trống và như vậy.

Cách đơn giản nhất để tránh trùng lặp là lặp lại tất cả các tệp và chọn từng tệp có 2/3 cơ hội, nhưng điều này không nhất thiết sẽ dẫn đến 200 tệp.

Điều này sẽ xóa một tệp nếu nó được chọn từ danh sách và đáp ứng các yêu cầu của bạn:

#!/bin/bash
files=( * )
# define 2/3 of them as sample size:
take=$((2*${#files[@]}/3)) 

while (( i < $take ))
do
    r=$((RANDOM%${#files[@]})) 
    f=${files[r]}
    if [[ -n $f ]]
    then 
        i=$((i+1))    
        echo ${files[r]}
        unset files[r]    
    fi
done

Bạn có thể chọn cùng một tệp nhiều lần.
glenn jackman

Kịch bản shell rất đẹp. Để giải quyết vấn đề không nhận được 200 tệp của bạn, có lẽ bạn muốn sử dụng Lấy mẫu lưu trữ: en.wikipedia.org/wiki/Reservoir_sampling Tôi sẽ yếu và không bao gồm ví dụ về tập lệnh shell.
Bruce Ediger

@glennjackman: Tôi đã viết như vậy, vâng. Cần vài phút để tìm hiểu, làm thế nào để loại bỏ các mục từ mảng.
người dùng không xác định

Thông báo trước nhỏ: $RANDOMchỉ có thể có các giá trị từ 0 đến 32767, do đó, điều này sẽ không hoạt động đúng nếu bạn có nhiều hơn 32768 tệp. Ngoài ra, tìm nạp được thiên về các tập tin đầu tiên.
l0b0

@ l0b0: Yêu cầu ở đâu, để chọn 200 từ 300. Nếu các tệp không có trong thư mục hiện tại, nhưng trên máy chủ tệp, nó cũng sẽ không hoạt động. Yêu cầu khác nhau, câu trả lời khác nhau.
người dùng không xác định

2

Nếu điều này cần phải được thống kê ngẫu nhiên, bạn không nên sử dụng RANDOM % ${#keys[@]}. Xem xét:

  1. $RANDOM có 32768 giá trị duy nhất
  2. Lựa chọn đầu tiên là 1 trong số 300 yếu tố
  3. 32768 = 109 * 300 + 68

Do đó, khi chọn mục đầu tiên, sẽ có 110/32768 ~ = 0,3569% cơ hội cho mỗi trong số 68 yếu tố đầu tiên và 109/236868 = = 0,3264% cơ hội cho mỗi trong số 232 yếu tố khác được chọn. Chọn được lặp đi lặp lại nhiều lần với các cơ hội khác nhau, nhưng thiên về các yếu tố đầu tiên bất cứ khi nào 32768 % ${#keys[@]} -ne 0, do đó, các hợp chất lỗi.

Điều này nên được thiên vị và hoạt động với bất kỳ tên tệp nào:

while IFS= read -r -d '' -u 9
do
    mv -- "$REPLY" /target/dir
done 9< <(find /source/dir -mindepth 1 -print0 | shuf -n 200 -z)

2

Giải pháp của Kevin hoạt động rất tốt! Một cái gì đó khác mà tôi đã sử dụng rất nhiều bởi vì nó dễ nhớ ra khỏi đỉnh đầu của tôi là một cái gì đó như:

cp `ls | shuf -n 200` destination

0

Một lớp lót trong bash:

ls original_directory/|sort -R|head -number_of_files_to_move|while read file; do cp "new_directory/"$file test; done

Xin hãy giải thích; U & L là một nền tảng kiến ​​thức.
phản biện
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.