我有一个包含许多子文件夹的目录,如下所示(请记住 FA 和 T1 内容是配对的,还有 ..._L、..._R 内容)。此处以字母(a、b、c 等)命名的文件夹数量可能会有很大差异:
.
├── FA
│ ├── CN
│ │ ├── CN_L
│ │ │ ├── GE_FSPGR
│ │ │ │ ├── a
│ │ │ │ └── b
│ │ │ ├── PHILIPS_MPRAGE
│ │ │ │ ├── c
│ │ │ │ └── d
│ │ │ └── SIEMENS_MPRAGE
│ │ │ ├── e
│ │ │ ├── f
│ │ │ ├── g
│ │ │ └── h
│ │ └── CN_R
│ │ ├── GE_FSPGR
│ │ │ ├── a
│ │ │ └── b
│ │ ├── PHILIPS_MPRAGE
│ │ │ ├── c
│ │ │ └── d
│ │ └── SIEMENS_MPRAGE
│ │ ├── e
│ │ ├── f
│ │ ├── g
│ │ └── h
│ └── Dementia
│ ├── Dementia_L
│ │ ├── GE_FSPGR
│ │ │ ├── i
│ │ │ └── j
│ │ ├── PHILIPS_MPRAGE
│ │ │ ├── k
│ │ │ └── l
│ │ └── SIEMENS_MPRAGE
│ │ ├── m
│ │ ├── n
│ │ ├── o
│ │ └── p
│ └── Dementia_R
│ ├── GE_FSPGR
│ │ ├── i
│ │ └── j
│ ├── PHILIPS_MPRAGE
│ │ ├── k
│ │ └── l
│ └── SIEMENS_MPRAGE
│ ├── m
│ ├── n
│ ├── o
│ └── p
└── T1
├── CN
│ ├── CN_L
│ │ ├── GE_FSPGR
│ │ │ ├── a
│ │ │ └── b
│ │ ├── PHILIPS_MPRAGE
│ │ │ ├── c
│ │ │ └── d
│ │ └── SIEMENS_MPRAGE
│ │ ├── e
│ │ ├── f
│ │ ├── g
│ │ └── h
│ └── CN_R
│ ├── GE_FSPGR
│ │ ├── a
│ │ └── b
│ ├── PHILIPS_MPRAGE
│ │ ├── c
│ │ └── d
│ └── SIEMENS_MPRAGE
│ ├── e
│ ├── f
│ ├── g
│ └── h
└── Dementia
├── Dementia_L
│ ├── GE_FSPGR
│ │ ├── i
│ │ └── j
│ ├── PHILIPS_MPRAGE
│ │ ├── k
│ │ └── l
│ └── SIEMENS_MPRAGE
│ ├── m
│ ├── n
│ ├── o
│ └── p
└── Dementia_R
├── GE_FSPGR
│ ├── i
│ └── j
├── PHILIPS_MPRAGE
│ ├── k
│ └── l
└── SIEMENS_MPRAGE
├── m
├── n
├── o
└── p
我想根据百分比将这些目录分为测试、训练和验证,这意味着取 60%、20% 和 20% 的子文件夹(a、b、c、d 等......也是文件夹)和将它们复制到相应的集合(训练、测试或验证),同时保留相同的文件夹父级和相同的配对结构。我不知道我是否表达清楚了。
输出将是这样的:
.
├── test
│ ├── FA
│ │ ├── CN
│ │ │ ├── CN_L
│ │ │ │ ├── GE_FSPGR
│ │ │ │ │ ├── a
│ │ │ │ ├── PHILIPS_MPRAGE
│ │ │ │ │ ├── c
│ │ │ │ └── SIEMENS_MPRAGE
│ │ │ │ ├── e
│ │ │ │ ├── f
│ │ │ └── CN_R
│ │ │ ├── GE_FSPGR
│ │ │ │ ├── a
│ │ │ ├── PHILIPS_MPRAGE
│ │ │ │ ├── c
│ │ │ └── SIEMENS_MPRAGE
│ │ │ ├── e
│ │ │ ├── f
│ │ └── Dementia
│ │ ├── Dementia_L
│ │ │ ├── GE_FSPGR
│ │ │ │ ├── i
│ │ │ ├── PHILIPS_MPRAGE
│ │ │ │ ├── k
│ │ │ └── SIEMENS_MPRAGE
│ │ │ ├── m
│ │ │ ├── n
│ │ └── Dementia_R
│ │ ├── GE_FSPGR
│ │ │ ├── i
│ │ ├── PHILIPS_MPRAGE
│ │ │ ├── k
│ │ └── SIEMENS_MPRAGE
│ │ ├── m
│ │ ├── n
│ └── T1
│ ├── CN
│ │ ├── CN_L
│ │ │ ├── GE_FSPGR
│ │ │ │ ├── a
│ │ │ ├── PHILIPS_MPRAGE
│ │ │ │ ├── c
│ │ │ └── SIEMENS_MPRAGE
│ │ │ ├── e
│ │ │ ├── f
│ │ └── CN_R
│ │ ├── GE_FSPGR
│ │ │ ├── a
│ │ ├── PHILIPS_MPRAGE
│ │ │ ├── c
│ │ └── SIEMENS_MPRAGE
│ │ ├── e
│ │ ├── f
│ └── Dementia
│ ├── Dementia_L
│ │ ├── GE_FSPGR
│ │ │ ├── i
│ │ ├── PHILIPS_MPRAGE
│ │ │ ├── k
│ │ └── SIEMENS_MPRAGE
│ │ ├── m
│ │ ├── n
│ └── Dementia_R
│ ├── GE_FSPGR
│ │ ├── i
│ ├── PHILIPS_MPRAGE
│ │ ├── k
│ └── SIEMENS_MPRAGE
│ ├── m
│ ├── n
├── train
│ ├── FA
│ │ ├── CN
│ │ │ ├── CN_L
│ │ │ │ ├── GE_FSPGR
│ │ │ │ │ ├── b
│ │ │ │ ├── PHILIPS_MPRAGE
│ │ │ │ │ ├── d
│ │ │ │ └── SIEMENS_MPRAGE
│ │ │ │ ├── g
│ │ │ │ ├── h
│ │ │ └── CN_R
│ │ │ ├── GE_FSPGR
│ │ │ │ ├── b
│ │ │ ├── PHILIPS_MPRAGE
│ │ │ │ ├── d
│ │ │ └── SIEMENS_MPRAGE
│ │ │ ├── g
│ │ │ ├── h
│ │ └── Dementia
│ │ ├── Dementia_L
│ │ │ ├── GE_FSPGR
│ │ │ │ ├── j
│ │ │ ├── PHILIPS_MPRAGE
│ │ │ │ ├── l
│ │ │ └── SIEMENS_MPRAGE
│ │ │ ├── o
│ │ │ ├── p
│ │ └── Dementia_R
│ │ ├── GE_FSPGR
│ │ │ ├── j
│ │ ├── PHILIPS_MPRAGE
│ │ │ ├── l
│ │ └── SIEMENS_MPRAGE
│ │ ├── o
│ │ ├── p
│ └── T1
│ ├── CN
│ │ ├── CN_L
│ │ │ ├── GE_FSPGR
│ │ │ │ ├── b
│ │ │ ├── PHILIPS_MPRAGE
│ │ │ │ ├── d
│ │ │ └── SIEMENS_MPRAGE
│ │ │ ├── g
│ │ │ ├── h
│ │ └── CN_R
│ │ ├── GE_FSPGR
│ │ │ ├── b
│ │ ├── PHILIPS_MPRAGE
│ │ │ ├── d
│ │ └── SIEMENS_MPRAGE
│ │ ├── g
│ │ ├── h
│ └── Dementia
│ ├── Dementia_L
│ │ ├── GE_FSPGR
│ │ │ ├── j
│ │ ├── PHILIPS_MPRAGE
│ │ │ ├── l
│ │ └── SIEMENS_MPRAGE
│ │ ├── o
│ │ ├── p
│ └── Dementia_R
│ ├── GE_FSPGR
│ │ ├── j
│ ├── PHILIPS_MPRAGE
│ │ ├── l
│ └── SIEMENS_MPRAGE
│ ├── o
│ ├── p
└── validation
├── FA
│ ├── CN
│ │ ├── CN_L
│ │ │ ├── GE_FSPGR
│ │ │ │ ├── aa
│ │ │ ├── PHILIPS_MPRAGE
│ │ │ │ ├── bb
│ │ │ └── SIEMENS_MPRAGE
│ │ │ ├── cc
│ │ │ ├── dd
│ │ └── CN_R
│ │ ├── GE_FSPGR
│ │ │ ├── aa
│ │ ├── PHILIPS_MPRAGE
│ │ │ ├── bb
│ │ └── SIEMENS_MPRAGE
│ │ ├── cc
│ │ ├── dd
│ └── Dementia
│ ├── Dementia_L
│ │ ├── GE_FSPGR
│ │ │ ├── ee
│ │ ├── PHILIPS_MPRAGE
│ │ │ ├── ff
│ │ └── SIEMENS_MPRAGE
│ │ ├── gg
│ │ ├── hh
│ └── Dementia_R
│ ├── GE_FSPGR
│ │ ├── ee
│ ├── PHILIPS_MPRAGE
│ │ ├── ff
│ └── SIEMENS_MPRAGE
│ ├── gg
│ ├── hh
└── T1
├── CN
│ ├── CN_L
│ │ ├── GE_FSPGR
│ │ │ ├── aa
│ │ ├── PHILIPS_MPRAGE
│ │ │ ├── bb
│ │ └── SIEMENS_MPRAGE
│ │ ├── cc
│ │ ├── dd
│ └── CN_R
│ ├── GE_FSPGR
│ │ ├── aa
│ ├── PHILIPS_MPRAGE
│ │ ├── bb
│ └── SIEMENS_MPRAGE
│ ├── cc
│ ├── dd
└── Dementia
├── Dementia_L
│ ├── GE_FSPGR
│ │ ├── ee
│ ├── PHILIPS_MPRAGE
│ │ ├── ff
│ └── SIEMENS_MPRAGE
│ ├── gg
│ ├── hh
└── Dementia_R
├── GE_FSPGR
│ ├── ee
├── PHILIPS_MPRAGE
│ ├── ff
└── SIEMENS_MPRAGE
├── gg
├── hh
我真的不知道该怎么做,如果你能帮助我,我将不胜感激。
我看过这个帖子通过子文件夹分发数千个文件,但我认为这不适合我的问题
谢谢
答案1
这外壳检查-clean Bash 程序可能适合您的需要:
#! /bin/bash -p
#### Utility functions
function quit
{
local -r msg=${1-unknown error}
printf '%s: ERROR: %s\n' "$0" "$msg" >&2
exit 1
}
# Do a random shuffle of the 'dirs' array
# (Code adapted from "BashFAQ/026 - Greg's Wiki",
# <https://mywiki.wooledge.org/BashFAQ/026>)
function shuffle_dirs
{
local i tmp size max rand
size=${#dirs[@]}
for ((i=size-1; i>0; i--)); do
# RANDOM % (i+1) is biased because of the limited range of $RANDOM
# Compensate by using a range which is a multiple of the rand modulus.
max=$(( 32768 / (i+1) * (i+1) ))
while (( (rand=RANDOM) >= max )); do :; done
rand=$(( rand % (i+1) ))
tmp=${dirs[i]} dirs[i]=${dirs[rand]} dirs[rand]=$tmp
done
return 0
}
# Copy an FA/..._L/... directory and directories paired with it under $srcdir
# to a "split" directory ('test', 'train', or 'validation') under $destdir
function copy_dirs
{
local -r splitdir=$1 # 'test', 'train', or 'validation'
local -r fa_l_dir=$2 # E.g. 'FA/CN/CN_L/GE_FSPGR/b'
local -a parts
local p0 p2 path
IFS=/ read -r -a parts <<<"$fa_l_dir"
for p0 in FA T1; do
for p2 in "${parts[2]}" "${parts[2]%_L}_R"; do
path="$p0/${parts[1]}/$p2/${parts[3]}/${parts[4]}"
srcpath="$srcdir/$path"
destpath="$destdir/$splitdir/$path"
mkdir --parents --verbose -- "${destpath%/*}"
cp --archive --verbose -- "$srcpath" "$destpath"
done
done
return 0
}
#### "main"
if (( $# != 2 )); then
printf 'usage: %s SRCDIR DESTDIR\n' "$0" >&2
exit 1
fi
declare -r srcdir=$1
declare -r destdir=$2
# Check for existence of required directories
for d in "$srcdir" "$srcdir/FA" "$srcdir/T1" "$destdir"; do
[[ -e $d ]] || quit "'$d' does not exist"
[[ -d $d ]] || quit "'$d' is not a directory"
done
shopt -s nullglob
for groupdir in "$srcdir"/FA/*/*_L/*/; do
# Get the list of FA/..._L/group/... directories to be copied
dirs=()
for d in "$groupdir"*; do
dirs+=( "${d#"$srcdir/"}" )
done
ndirs=${#dirs[*]}
if (( ndirs == 0 )); then
printf "%s: WARNING: no directories match '$groupdir*'\\n" "$0" >&2
continue
fi
# Randomly shuffle the list of directories to randomize which directories
# get copied to which locations
shuffle_dirs
declare -i pc_sum=0 lo=0 hi
for splitdir_pc in test:60 train:20 validation:20; do
splitdir=${splitdir_pc%:*}
pc=${splitdir_pc#*:}
pc_sum+=$pc
hi='(ndirs * pc_sum + 50)/100 - 1'
for ((i=lo; i<=hi; i++)); do
copy_dirs "$splitdir" "${dirs[i]}"
done
lo='hi+1'
done
done
exit 0
- 该程序采用两个命令行参数:
FA
包含和目录的目录的路径T1
。test
包含、train
和validation
目录(以及将在其下复制的目录)的目录路径。
- 如果您有兴趣,我很乐意向代码添加文档或回答您的任何问题。
答案2
计算路径名或文件内容的哈希值。这是一个哈希函数示例。
$ date | shasum
7ae62fd4e6483c966fd23d8eeafabc934226914d -
取第一个字符:... shasum | cut -c1
创建十六个符号链接,大致对应于您所需的百分比。将它们指向您的三个现有目录。
ln -s /data/test 0
...
ln -s /data/test 9
ln -s /data/train a
ln -s /data/train b
ln -s /data/train c
ln -s /data/validate d
ln -s /data/validate e
ln -s /data/validate f
现在很容易了。获取哈希的初始字符以及cp
该目标目录的 src 文件。
您将获得每个类别中大约正确数量的文件。如果您更喜欢 1/256 而不是 1/16 的分辨率,请随意为每个哈希的前两个字符创建符号链接。
编辑
你说你有一堆文件,例如
FA/CN/CN_L/GE_FSPGR/a/one.csv
FA/CN/CN_L/GE_FSPGR/a/two.csv
...
FA/CN/CN_L/GE_FSPGR/a/one_hundred.csv
您希望为每个文件选择三个条件之一:训练、测试、验证。
让我们散列一个特别的路径名:
$ echo FA/CN/CN_L/GE_FSPGR/a/one.csv | shasum
16930ea7216d650735ea7c223a67371c5a5ed770 -
$
$ echo FA/CN/CN_L/GE_FSPGR/a/one.csv | shasum | cut -c1
1
对其他文件重复此操作会产生d
和2
。
已设置的几个符号链接将导致$ cp one.csv 1
和$ cp one_hundred.csv 2
降落在“测试”区域,并将$ cp two.csv d
成为验证数据的一部分。