2016-08-29 146 views
1

我正在嘗試創建一個安裝腳本,能夠爲我設置一個工作區,這樣我就不需要手動完成了。 我開始在bash中做這件事,但很快就意識到那不會很好。隨機分配文件到列車/測試給定的比例

我的下一個想法是使用python,但似乎無法做到這一點正確的方法..我的想法是做一個列表(列表是一個.txt文件與所有數據文件的路徑) ,洗牌這個列表,然後將每個文件移動到我的火車目錄或測試目錄,給定的比例....

但這是python,是不是有一個更簡單的方法來做到這一點,它似乎是我正在做一個理解解決方法只是爲了分割文件。

bash代碼:

# Partition data randomly into train and test. 
cd ${PATH_TO_DATASET} 
SPLIT=0.5 #train/test split 
NUMBER_OF_FILES=$(ls ${PATH_TO_DATASET} | wc -l) ## number of directories in the dataset 
even=1 
echo ${NUMBER_OF_FILES} 

if [ `echo "${NUMBER_OF_FILES} % 2" | bc` -eq 0 ] 
then  
     even=1 
     echo "Even is true" 
else 
     even=0 
     echo "Even is false" 
fi 

echo -e "${BLUE}Seperating files in to train and test set!${NC}" 

for ((i=1; i<=${NUMBER_OF_FILES}; i++)) 
do 
    ran=$(python -c "import random;print(random.uniform(0.0, 1.0))")  
    if [[ ${ran} < ${SPLIT} ]] 
    then 
     ##echo "test ${ran}" 
     cp -R $(ls -d */|sed "${i}q;d") ${WORKSPACE_SETUP_ROOT}/../${WORKSPACE}/data/test/ 
    else 
     ##echo "train ${ran}"  
     cp -R $(ls -d */|sed "${i}q;d") ${WORKSPACE_SETUP_ROOT}/../${WORKSPACE}/data/train/ 
    fi 

    ##echo $(ls -d */|sed "${i}q;d") 
done  

cd ${WORKSPACE_SETUP_ROOT}/../${WORKSPACE}/data 
NUMBER_TRAIN_FILES=$(ls train/ | wc -l) 
NUMBER_TEST_FILES=$(ls test/ | wc -l) 

echo "${NUMBER_TRAIN_FILES} and ${NUMBER_TEST_FILES}..." 
echo $(calc ${NUMBER_TRAIN_FILES}/${NUMBER_OF_FILES}) 

if [[ ${even} = 1 ]] && [[ ${NUMBER_TRAIN_FILES}/${NUMBER_OF_FILES} != ${SPLIT} ]] 
    then 
    echo "Something need to be fixed!" 
    if [[ $(calc ${NUMBER_TRAIN_FILES}/${NUMBER_OF_FILES}) > ${SPLIT} ]] 
    then 
     echo "Too many files in the TRAIN set move some to TEST" 
     cd train 
     echo $(pwd) 
     while [[ ${NUMBER_TRAIN_FILES}/${NUMBER_TEST_FILES} != ${SPLIT} ]] 
     do 
      mv $(ls -d */|sed "1q;d") ../test/ 
      echo $(calc ${NUMBER_TRAIN_FILES}/${NUMBER_OF_FILES}) 
     done 
    else 
     echo "Too many files in the TEST set move some to TRAIN" 
     cd test 
     while [[ ${NUMBER_TRAIN_FILES}/${NUMBER_TEST_FILES} != ${SPLIT} ]] 
     do 
      mv $(ls -d */|sed "1q;d") ../train/ 
      echo $(calc ${NUMBER_TRAIN_FILES}/${NUMBER_OF_FILES}) 
     done 
    fi 

fi 

我的問題是最後一部分。由於我隨機選取數字,我不確定數據是否按照希望分區,我最後一條陳述是檢查分區是否正確,如果不正確,則修復它。這是不可能的,因爲我正在檢查浮點數,通常這個解決方案更像是一個快速修復。

+0

我很想看看一些示例數據和你在bash中遇到的問題。 「分配」是什麼意思?你在移動文件嗎?將數據插入數組?如果您還可以包含更多關於您用來確定發生的標準的信息,那麼我們可能會提供有用的答案。 – ghoti

+0

數據只是.wav文件。我的bash代碼的問題是我試圖使用浮點運算,這對於bash來說並不理想。我正在將它從_data_文件夾移動到_train_或_test_文件夾 –

+0

好吧,那麼您使用什麼標準來決定是將某些內容發送到一個文件夾還是另一個文件夾?你能否在你的問題中包含你的非工作代碼? – ghoti

回答

5

scikit-learn來救援=)

>>> import numpy as np 
>>> from sklearn.cross_validation import train_test_split 
>>> X, y = np.arange(10).reshape((5, 2)), range(5) 
>>> X 
array([[0, 1], 
     [2, 3], 
     [4, 5], 
     [6, 7], 
     [8, 9]]) 
>>> y 
[0, 1, 2, 3, 4] 


# If i want 1/4 of the data for testing 
# and i set a random seed of 42. 
>>> X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42) 
>>> X_train 
array([[4, 5], 
     [0, 1], 
     [6, 7]]) 
>>> X_test 
array([[2, 3], 
     [8, 9]]) 
>>> y_train 
[2, 0, 3] 
>>> y_test 
[1, 4] 

http://scikit-learn.org/stable/modules/generated/sklearn.cross_validation.train_test_split.html


爲了證明:

[email protected]:~$ mkdir splitfileproblem 
[email protected]:~$ cd splitfileproblem/ 
[email protected]:~/splitfileproblem$ mkdir original 
[email protected]:~/splitfileproblem$ mkdir train 
[email protected]:~/splitfileproblem$ mkdir test 
[email protected]:~/splitfileproblem$ ls 
original train test 
[email protected]:~/splitfileproblem$ cd original/ 
[email protected]:~/splitfileproblem/original$ ls 
[email protected]:~/splitfileproblem/original$ echo 'abc' > a.txt 
[email protected]:~/splitfileproblem/original$ echo 'def\nghi' > b.txt 
[email protected]:~/splitfileproblem/original$ cat a.txt 
abc 
[email protected]:~/splitfileproblem/original$ echo -e 'def\nghi' > b.txt 
[email protected]:~/splitfileproblem/original$ cat b.txt 
def 
ghi 
[email protected]:~/splitfileproblem/original$ echo -e 'jkl' > c.txt 
[email protected]:~/splitfileproblem/original$ echo -e 'mno' > d.txt 
[email protected]:~/splitfileproblem/original$ ls 
a.txt b.txt c.txt d.txt 

在Python:

[email protected]:~/splitfileproblem$ ls 
original test train 
[email protected]:~/splitfileproblem$ python 
Python 2.7.12 (default, Jul 1 2016, 15:12:24) 
[GCC 5.4.0 20160609] on linux2 
Type "help", "copyright", "credits" or "license" for more information. 
>>> import os 
>>> from sklearn.cross_validation import train_test_split 
>>> os.listdir('original') 
['b.txt', 'd.txt', 'c.txt', 'a.txt'] 
>>> X = y= os.listdir('original') 
>>> X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=0) 
>>> X_train 
['a.txt', 'd.txt', 'b.txt'] 
>>> X_test 
['c.txt'] 

現在移動文件:

>>> for x in X_train: 
...  os.rename('original/'+x , 'train/'+x) 
... 
>>> for x in X_test: 
...  os.rename('original/'+x , 'test/'+x) 
... 
>>> os.listdir('test') 
['c.txt'] 
>>> os.listdir('train') 
['b.txt', 'd.txt', 'a.txt'] 
>>> os.listdir('original') 
[] 

參見:How to move a file in Python

+0

這些文件沒有加載到Python ...他們的實際文件需要從A移動到B .. –

+0

很高興看到bash解決方案,我懷疑它涉及'shuffle','mv', 'awk','ls' =) – alvas

+0

這裏的問題是文件必須被隨機地分成列車和測試,具有給定的比率/分割。 –

2

這裏的第一個乾式切削的解決方案,純Python:

import sys, random, os 

def splitdirs(files, dir1, dir2, ratio): 
    shuffled = files[:] 
    random.shuffle(shuffled) 
    num = round(len(shuffled) * ratio) 
    to_dir1, to_dir2 = shuffled[:num], shuffled[num:] 
    for d in dir1, dir2: 
     if not os.path.exists(d): 
      os.mkdir(d) 
    for file in to_dir1: 
     os.symlink(file, os.path.join(dir1, os.path.basename(file))) 
    for file in to_dir2: 
     os.symlink(file, os.path.join(dir2, os.path.basename(file))) 

if __name__ == '__main__': 
    if len(sys.argv) != 5: 
     sys.exit('Usage: {} files.txt dir1 dir2 ratio'.format(sys.argv[0])) 
    else: 
     files, dir1, dir2, ratio = sys.argv[1:] 
     ratio = float(ratio) 
     files = open(files).read().splitlines() 
     splitdirs(files, dir1, dir2, ratio) 

[[email protected] ~]$ python ./test.py ./files.txt dev tst 0.4 這裏的文件中列出的40% .txt去開發目錄,並且60% - 到tst

它使得symliks而不是副本,如果你需要真正的文件,更改os.symlinkshutil.copy2

2

下面是一個使用bash的$RANDOM的東西移到兩個目標目錄中的一個簡單的例子。

$ touch {1..10} 
$ mkdir red blue 
$ a=(*/) 
$ RANDOM=$$ 
$ for f in [0-9]*; do mv -v "$f" "${a[$((RANDOM/(32768/${#a[@]})))]}"; done 
1 -> red/1 
10 -> red/10 
2 -> blue/2 
3 -> red/3 
4 -> red/4 
5 -> red/5 
6 -> red/6 
7 -> blue/7 
8 -> blue/8 
9 -> blue/9 

本例首先創建10個文件和兩個目標目錄。它將數組設置爲*/,該數組展開爲「當前目錄內的所有目錄」。然後它運行一個for循環,看起來像線噪聲。我會分開爲你分開。

"${a[$((RANDOM/(32768/${#a[@]})+1))]}"是:

  • ${a[ ...數組「a」,
  • $((...)) ...其下標爲整數數學函數。
  • $RANDOM是產生ramdom(ISH)編號從0到32767一個bash可變的,並且我們的公式通過除以該比率的分母:
  • ${#a[@]},由陣列中元件的數量有效地乘以RANDOM/32768「一」。

所有這些的結果是我們選擇隨機數組元素a.k.a.隨機目錄。

如果你真的想從你的「文件列表」工作,並假設你離開你的陣列「一個」潛在目標名單,你可以用while循環替換for循環:

while read f; do 
    mv -v "$f" "${a[$((RANDOM/(32768/${#a[@]})))]}" 
done < /dir/file.txt 

現在......這些解決方案將結果「均勻地」分開。當分母增加時會發生這種情況。因爲它們是隨機的,所以沒有辦法確保你的random numbers won't put all your files into a single directory。所以要分手,你需要更有創意。

假設我們正在處理的只有兩個目標(因爲我認爲這是你在做什麼)。如果您正在尋找25/75分割,請相應地切分隨機數範圍。

$ declare -a b=([0]="red/" [8192]="blue/") 
$ for f in {1..10}; do n=$RANDOM; for i in "${!b[@]}"; do [ $i -gt $n ] && break; o="${b[i]}"; done; mv -v "$f" "$o"; done 

爆發更容易閱讀,這裏我們有什麼,有評論說:

declare -a b=([0]="red/" [8192]="blue/") 

for f in {1..10}; do   # Step through our files... 
    n=$RANDOM     # Pick a random number, 0-32767 
    for i in "${!b[@]}"; do # Step through the indices of the array of targets 
    [ $i -gt $n ] && break # If the current index is > than the random number, stop. 
    o="${b[i]}"    # If we haven't stopped, name this as our target, 
    done 
    mv -v "$f" "$o"   # and move the file there. 
done 

我們使用數組的索引定義我們的分裂。 8192是32767的25%,最大值爲$ RANDOM。但是你喜歡這個範圍內,包括其中超過2

如果你想測試這種方法的結果,在陣列中的計數結果是一個辦法做到這一點,你可能分裂的事情。讓我們構建一個shell函數來幫助測試。

$ tester() { declare -A c=(); for f in {1..10000}; do n=$RANDOM; for i in "${!b[@]}"; do [ $i -gt $n ] && break; o="${b[i]}"; done; ((c[$o]++)); done; declare -p c; } 
$ declare -a b='([0]="red/" [8192]="blue/")' 
$ tester 
declare -A c='([blue/]="7540" [red/]="2460")' 
$ b=([0]="red/" [10992]="blue/") 
$ tester 
declare -A c='([blue/]="6633" [red/]="3367")' 

在第一行中,我們定義了我們的函數。第二行將「b」數組設置爲25/75分割,然後我們運行該函數,其輸出是計數器陣列。然後我們重新定義一個33/67分割的「b」數組,然後再次運行該函數來演示結果。

所以......雖然你肯定可能使用python這個,你幾乎可以肯定地實現你需要什麼與bash和一個小數學。