Bash - 列中的交换值

Ran*_*man 6 linux bash awk grep sed

我在文件中有一些CSV /表格数据,如下所示:

1,7,3,2
8,3,8,0
4,9,5,3
8,5,7,3
5,6,1,9
Run Code Online (Sandbox Code Playgroud)

(它们并不总是数字,只是随机逗号分隔的值.但是,例如,单位数字更容易.)

我想随机抽取40%的任何一列.举个例子,说第3个.所以也许3和1相互交换.现在第三列是:

1 << Came from the last position
8
5
7
3 << Came from the first position
Run Code Online (Sandbox Code Playgroud)

我正在尝试在bash我正在处理的脚本中的文件中执行此操作,并且我没有太多运气.我一直在徘徊一些非常疯狂和没有结果的grep兔子洞让我觉得我走错路(不断失败就是让我失望).

我用一连串的东西标记了这个问题,因为我不完全确定我应该使用哪种工具.

编辑:我可能最终会接受鲁本斯的答案,不过它很古怪,因为它直接包含交换概念(我想我原本可以在原问题中强调过),它允许我指定一个百分比用于交换的列.它也恰好工作,这总是一个加号.

对于那些不需要这个,只想要一个基本的洗牌的人,Jim Garrison的答案也有效(我测试了它).

但是,关于鲁本斯解决方案的警告.我拿了这个:

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

删除printf "\n";并移动换行符如下:

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

因为只是""在else的情况下导致awk在每一行(\00)的末尾写入破碎的字符.有一次,它甚至设法用中文字符替换我的整个文件.虽然,老实说,这可能涉及到我在这个问题上做一些额外的愚蠢.

Rub*_*ens 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
    
    Run Code Online (Sandbox Code Playgroud)

执行

#! /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}
Run Code Online (Sandbox Code Playgroud)

如果您想要一种接近就地放置的方法,您可以使用Sponge将输出通过管道传输输入文件。

执行

要执行,只需使用:

$ ./script.sh <inpath> <column> <percentage>
Run Code Online (Sandbox Code Playgroud)

如:

$ ./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
Run Code Online (Sandbox Code Playgroud)

结论

这允许您选择列,随机排序该列中一定百分比的条目,并替换原始文件中的新列。

这个脚本就像其他脚本一样,不仅证明 shell 脚本非常有趣,而且在某些情况下绝对应该使用它。(: