6

次のように、ファイルにCSV/表形式のデータがあります。

1,7,3,2
8,3,8,0
4,9,5,3
8,5,7,3
5,6,1,9

(これらは常に数字ではなく、ランダムなコンマ区切りの値です。ただし、例としては1桁の数字の方が簡単です。)

任意の列の40%をランダムにシャッフルしたいと思います。例として、3番目のものを言います。したがって、おそらく3と1は互いに交換されます。3番目の列は次のとおりです。

1 << Came from the last position
8
5
7
3 << Came from the first position

作業中のスクリプト内からファイル内でこれを実行しようとしていますが、bashあまり運がありません。私はかなりクレイジーで実りのgrepないウサギの穴をさまよっているので、私は間違った方向に進んでいると思います(絶え間ない失敗が私をひっくり返すものです)。

どのツールを使用すべきか完全にはわからないので、この質問にたくさんのタグを付けました。

編集:おそらくルーベンスの答えを受け入れることになりますが、それはスワッピングの概念が直接含まれているため(元の質問でもっと強調できたと思います)、パーセンテージを指定できるため、奇妙です交換用の列の。それはたまたま機能しますが、これは常にプラスです。

これを必要とせず、基本的なシャッフルが必要な人には、ジム・ギャリソンの答えも機能します(私はそれをテストしました)。

しかし、ルーベンスの解決策についての警告の言葉。私はこれを取りました:

for (i = 1; i <= NF; ++i) {
  delim = (i != NF) ? "," : "";
  ...
}
printf "\n";

を削除しprintf "\n";、改行文字を次のように上に移動しました。

for (i = 1; i <= NF; ++i) {
  delim = (i != NF) ? "," : "\n";
  ...
}

""elseケースを使用するだけでawk、各行の終わりに壊れた文字が書き込まれるためです(\00)。ある時点で、ファイル全体を漢字に置き換えることさえできました。正直なところ、これにはおそらく私がこの問題に加えてさらに愚かなことをすることが含まれていました。

4

3 に答える 3

4

これは、特別に指定された列で機能しますが、正しい方向を示すのに十分なはずです。これは、Cygwinを含む最新のbashシェルで機能します。

paste -d, <(cut -d, -f1-2 test.dat) <(cut -d, -f3 test.dat|shuf) <(cut -d, -f4- test.dat)

運用上の特徴は「プロセス置換」です。

コマンドはファイルを水平方向に結合し、paste3つの部分は元のファイルからを介して分割されcut、2番目の部分(ランダム化される列)がshufコマンドを実行して行を並べ替えます。これを数回実行した結果は次のとおりです。

$ cat test.dat
1,7,3,2
8,3,8,0
4,9,5,3
8,5,7,3
5,6,1,9

$ paste -d, <(cut -d, -f1-2 test.dat) <(cut -d, -f3 test.dat|shuf) <(cut -d, -f4- test.dat)
1,7,1,2
8,3,8,0
4,9,7,3
8,5,3,3
5,6,5,9

$ paste -d, <(cut -d, -f1-2 test.dat) <(cut -d, -f3 test.dat|shuf) <(cut -d, -f4- test.dat)
1,7,8,2
8,3,1,0
4,9,3,3
8,5,7,3
5,6,5,9
于 2013-03-19T05:46:59.827 に答える
1

アルゴリズム

  • からまでのnペアと、(選択した列の)行のそれぞれの値を使用してベクトルを作成し、ランダムに並べ替えます。1number of lines
  • ランダム化する必要のある行数を見つけます:num_random = percentage * num_lines / 100;
  • num_randomランダム化されたベクトルから最初のエントリを選択します。
  • 選択した行をランダムに並べ替えることができますが、すでにランダムに並べ替えられているはずです。
  • 印刷出力:

    i = 0
    for num_line, value in column; do
        if num_line not in random_vector:
            print value; # printing non-randomized value
        else:
            print random_vector[i]; # randomized entry
            i++;
    done
    

実装

#! /bin/bash

infile=$1
col=$2
n_lines=$(wc -l < ${infile})
prob=$(bc <<< "$3 * ${n_lines} / 100")

# Selected lines
tmp=$(tempfile)
paste -d ',' <(seq 1 ${n_lines}) <(cut -d ',' -f ${col} ${infile}) \
    | sort -R | head -n ${prob} > ${tmp}

# Rewriting file
awk -v "col=$col" -F "," '
(NR == FNR) {id[$1] = $2; next}
(FNR == 1) {
    i = c = 1;
    for (v in id) {value[i] = id[v]; ++i;}
}
{
    for (i = 1; i <= NF; ++i) {
        delim = (i != NF) ? "," : "";
        if (i != col) {printf "%s%c", $i, delim; continue;}
        if (FNR in id) {printf "%s%c", value[c], delim; c++;}
        else {printf "%s%c", $i, delim;}
    }
    printf "\n";
}
' ${tmp} ${infile}

rm ${tmp}

インプレースメントに近いアプローチが必要な場合は、スポンジを使用して、出力を入力ファイルにパイプで戻すことができます。

実行

実行するには、次を使用します。

$ ./script.sh <inpath> <column> <percentage>

のように:

$ ./script.sh infile 3 40
1,7,3,2
8,3,8,0
4,9,1,3
8,5,7,3
5,6,5,9

結論

これにより、列を選択し、その列のエントリの割合をランダムに並べ替えて、元のファイルの新しい列を置き換えることができます。

このスクリプトは、シェルスクリプトが非常に面白いだけでなく、絶対に使用すべきではない場合があることを証明するものです。(:

于 2013-03-19T07:38:59.430 に答える
0

行数のカウントを取得してファイルを配列に読み込むことから始め、次にawkのrand()関数を使用して乱数を生成し、変更する行を識別してからrandする2パスアプローチを使用します。 ()もう一度、これらの行のどのペアを交換するかを決定し、印刷する前に配列要素を交換します。この擬似コードのようなもの、大まかなアルゴリズム:

awk -F, -v pct=40 -v col=3 '
NR == FNR {
    array[++totNumLines] = $0
    next
}

FNR == 1{
    pctNumLines = totNumLines * pct / 100

    srand()

    for (i=1; i<=(pctNumLines / 2); i++) {
        oldLineNr = rand() * some factor to produce a line number that's in the 1 to totNumLines range but is not already recorded as processed in the "swapped" array.
        newLineNr = ditto plus must not equal oldLineNr

        swap field $col between array[oldLineNr] and array[newLineNr]

        swapped[oldLineNr]
        swapped[newLineNr]
    }
    next
}

{ print array[FNR] }

' "$file" "$file" > tmp &&
mv tmp "$file"
于 2013-03-19T12:26:19.650 に答える