2016-08-29 147 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和一个小数学。