2013-03-19 39 views
6

交換價值,我有一些CSV /表格數據在一個文件中,像這樣:巴什 - 在

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

(他們並不總是數字,只是隨機逗號分隔值個位數號碼。儘管如此,還是比較容易的。)

我想隨機洗牌40%的任何列。舉個例子,說第三個。所以也許3和1互相交換。現在,第三列是:

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

我試圖從bash腳本,我的工作中的一個文件來做到這一點的地方,我沒有多少運氣。我一直徘徊在一些非常瘋狂和沒有結果的兔子洞口,這讓我以爲我走錯了路(不斷的失敗是什麼讓我不知所措)。

我用一連串的東西標記了這個問題,因爲我不完全確定我應該爲此使用哪個工具。

編輯:我可能會最終接受魯本斯的答案,但古怪的是,因爲它直接包含了交換的概念(我想我可以強調在原來的問題更多),它允許我指定交換列的百分比。它也適用於工作,這總是一個加號。

對於不需要這個的人,只是想要一個基本的洗牌,Jim Garrison的答案也有效(我測試了它)。

但是,魯本斯的解決方案的警告。我把這個:

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

取出printf "\n";和移動換行符像這樣:

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

,因爲只是在其他情況下,具有""是造成awk在每年年底寫斷字行(\00)。有一次,它甚至設法用中文字符替換我的整個文件。雖然,說實話,這可能讓我在這個問題上做了一些額外的愚蠢行爲。

+1

隨機化並不的強度文本處理工具,如'sed'或'awk' – 2013-03-19 04:52:55

+1

你想選擇40%的列並完全洗牌,或者選擇一個(或多個)列並隨機洗牌40%? – FoolishSeth 2013-03-19 05:27:43

+0

後者(40%一排柱子N)。 – 2013-03-19 05:28:49

回答

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} 

如果你想有一個貼近於貼裝,你可以管的輸出到輸入文件,使用sponge

執行

要執行,只需使用:

$ ./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 

結論

這允許您選擇合作lumn,隨機對該列中的百分比進行排序,並且替換原始文件中的新列。

該腳本與其他腳本一樣,不僅僅是shell腳本非常有趣,但有些情況下它肯定會被使用而不是。 (:

4

這將適用於專門指定的專欄,但應該足以讓您指向正確的方向。這適用於現代的bash殼包括Cygwin的:

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

手術的特點是 「process substitution」。

paste命令水平加入的文件,並且三個片從原始文件經由cut分割,通過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 
+0

+1'shuf'可能需要用一個自定義的混洗器來替換,以處理40%的約束,但是否則很好(假設列數固定)。 – chepner 2013-03-19 12:11:20

0

我會使用2遍方法,首先獲取行數並將文件讀入數組,然後使用awk的rand()函數生成隨機數以標識您的行「會再次改變,然後蘭特()以確定將交換其對那些行中,然後在打印前交換陣列元件像這樣的僞代碼,粗糙算法:

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"