시뮬레이션 데이터 생성 개선

시뮬레이션 데이터 생성 개선

시뮬레이션된 데이터를 사용하여 CSV를 생성하려고 합니다.

for i in {1..1000000..1}
do 
  echo "$i,$(date -d "2017-08-01 + $(shuf -i 1-31 -n 1) days" +'%Y-%m-%d')" >> $F
done;

1에서 백만까지 반복하고 고유 ID 및 생성무작위의날짜

하지만 매우 느리게 실행됩니다. 평행하게 만들 수 있는 단일 선이 있습니까?

답변1

마지막에 최종 결과를 확인하세요.

for i in {1..1000000..1}
do 
  echo "$i,$(date -d "2017-08-01 + $(shuf -i 1-31 -n 1) days" +'%Y-%m-%d')" >> $F
done;

셸 루프는 느리며 이 특정 루프를 특히 느리게 만드는 두 가지 주요 요인이 있습니다.

  1. 반복할 때마다 파일을 열고 추가합니다.
  2. 외부 유틸리티( shufand)는 각 반복에서 두 번 실행됩니다. date이는 echo아마도 쉘에 내장되어 있으므로 오버헤드가 덜 발생합니다.

출력 리디렉션은 해결하기 가장 쉽습니다.

for i in {1..1000000..1}
do 
  echo "$i,$(date -d "2017-08-01 + $(shuf -i 1-31 -n 1) days" +'%Y-%m-%d')" 
done >"$F"

그러면 출력 파일이 한 번만 열리고 루프 중에 열린 상태로 유지됩니다.


나머지 코드는 awkGNU를 사용하여 더 효율적으로 수행 할 수 있습니다 date(사용하고 있으므로 shufLinux 시스템을 사용하고 있다고 가정하므로 date실제로는 GNU일 가능성이 높습니다 date).

awk 'END { for (i=0;i<100;++i) { printf("2017-08-01 + %d days\n", 1+int(31*rand())) }}' /dev/null

그러면 다음과 같은 100줄이 생성됩니다.

2017-08-01 + 22 days
2017-08-01 + 31 days
2017-08-01 + 11 days
2017-08-01 + 27 days
2017-08-01 + 27 days
2017-08-01 + 20 days
(etc.)

이것을 GNU로 가져오자 date. GNU에는 프로그램 이 출력하는 날짜 사양 과 같은 여러 날짜 사양을 일괄 입력할 수 있는 date플래그가 있습니다 .-fawk

awk 'END { for (i=0;i<100;++i) { printf("2017-08-01 + %d days\n", 1+int(31*rand())) }}' /dev/null |
date -f - +'%Y-%m-%d'

이제 우리는 얻습니다

2017-08-23
2017-08-27
2017-08-21
2017-08-29
2017-08-25
2017-08-17
2017-08-07
(etc.)

그런 다음 각 행에 고유 ID(연속된 정수)를 추가하면 됩니다.

awk 'END { for (i=0;i<100;++i) { printf("2017-08-01 + %d days\n", 1+int(31*rand())) }}' /dev/null |
date -f - +'%Y-%m-%d' |
awk -vOFS=',' '{ print NR, $0 }'

이건 널위한거야

1,2017-08-06
2,2017-08-17
3,2017-08-25
4,2017-08-28
5,2017-08-14
6,2017-08-15
7,2017-08-17
8,2017-08-10
9,2017-08-16
10,2017-08-08
(etc.)

이제 끝났습니다. 그 과정에서 나는 쉘 루프가 있다는 사실을 완전히 잊어버렸습니다. 필요하지 않은 것으로 밝혀졌습니다.

100원하는 값으로 설정 하고 필요에 맞게 난수 생성기를 조정하세요. rand()0 <= 숫자 < 1이 되는 부동 소수점 값을 반환합니다.


분명히, 8월(31일로 구성된 달)에 임의의 날짜를 원하는 경우 date완전히 우회할 수 있습니다.

awk 'END { for (i=1;i<=100;++i) { printf("%d,2017-08-%02d\n", i, 1+int(31*rand())) }}' /dev/null

BSD가 아닌 GNU awk및 Mike의 awk( )를 사용하면 다음에서 직접 올바른 날짜 처리를 수행할 수도 있습니다 .mawkawkawk

awk 'END { for (i=1;i<=100;++i) { printf("%d,%s\n", i, strftime("%Y-%m-%d", 1501545600 + int(2678400*rand()),1 )) }}' /dev/null

이제 우리는 날짜 대신 Unix 타임스탬프를 다루고 있습니다. 1501545600은 "2017년 8월 1일 화요일 00:00:00 UTC"에 해당하며, 이는 31일 동안 2678400초입니다.

답변2

# A "random" date between 2000-01-01 and 2025-12-28
# Only uses day 01 to 28 
rand_date() {
    printf "%4d-%02d-%02d" $((RANDOM%25+2000)) $((RANDOM%12+1)) $((RANDOM%28+1))
}

csv_data() {
    for ((i=1; i<="$1"; i++)); do printf "%d,%s\n" $i $(rand_date); done
}
$ time (csv_data 1000000 > data.csv)
real    7m26.683s
user    0m36.376s
sys 1m57.768s

Perl이 더 빠를 수도 있습니다. 시도해 보겠습니다.

$ cat data.pl
#!/usr/bin/perl
$, = ",";
$\ = "\n";

sub rand_date {
    sprintf "%4d-%02d-%02d", int(rand(25))+2000, int(rand(12))+1, int(rand(28))+1;
}

sub csv_data {
    my $n = shift;
    for ($i = 1; $i <= $n; $i++) {
        print $i, rand_date();
    }
}

csv_data(1_000_000);
$ time (perl data.pl > data.csv)

real    0m0.881s
user    0m0.876s
sys 0m0.004s

응, 더 빨리...

관련 정보