シミュレートされたデータを使用して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から100万まで繰り返し、一意の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;
シェルループは遅く、この特定のループを特に遅くする2つの主な要因があります。
- 繰り返すたびにファイルを開いて追加します。
- 外部ユーティリティ(
shuf
and)は各反復で2回実行されます。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"
これにより、出力ファイルが一度だけ開かれ、ループ中に開いたままになります。
残りのコードはawk
GNUを使用してより効率的に実行できますdate
(使用しているので、shuf
Linuxシステムを使用していると仮定するので、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
フラグがあります。-f
awk
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ではなくGNUawk
とMikeのawk
()を使用すると、次のように直接正しい日付処理を実行することもできます。mawk
awk
awk
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
うん、もっと早く...