如何按比例将两个随机数中的一个添加到 csv 的每一行?



我有一个csv,它包含100行乘以三列随机数:

100, 20, 30
746, 82, 928
387, 12, 287.3
12, 47, 2938
125, 198, 263
...
12, 2736, 14

在 bash 中,我需要添加另一列,该列将是 0 或 1。但是,(这是困难的部分(,我需要有 20% 的行带有 0,80% 的行带有 1。 结果:

100, 20, 30, 0
746, 82, 928, 1
387, 12, 287.3, 1
12, 47, 2938, 1
125, 198, 263, 0
...
12, 2736, 14, 1

我尝试过的:

sed '1~3s/$/0/' mycsv.csv

但我想我可以用"随机数"替换 1~3,但这不起作用。 也许循环会?也许是sed或awk?

使用 awk 和rand()随机获得 0 和 1,获得 0 的概率为 20%:

$ awk 'BEGIN{OFS=", ";srand()}{print $0,(rand()>0.2)}' file

输出:

100, 20, 30, 1
746, 82, 928, 1
387, 12, 287.3, 1
12, 47, 2938, 0
125, 198, 263, 1
..., 0
12, 2736, 14, 1

解释:

$ awk '
BEGIN {
OFS=", "                 # set output field separator
srand()                  # time based seed for rand()
}
{
print $0,(rand()>0.2)    # output 0/1 ~ 20/80
}' file

由于srand()本身是基于时间(秒(的,因此根据需要,您可能希望为其引入外部种子,例如,从 Bash :

$ awk -v seed=$RANDOM 'BEGIN{srand(seed)}...'

更新:首先计算文件中的行数,计算出 20% 0 的行数并随机选取 0 或 1 并保持计数的版本:

$ awk -v seed=$RANDOM '
BEGIN {
srand(seed)                               # feed the seed to random
}
NR==1 {                                       # processing the first record
while((getline line < FILENAME)>0)        # count the lines in the file
nr++                                  # nr stores the count
for(i=1;i<=nr;i++)                        # produce 
a[(i>0.2*nr)]++                       # 20 % 0s, 80 % 1s
}
{
p=a[0]/(a[0]+a[1])                        # probability to pick 0 or 1
print $0 ". " (a[v=(rand()>p)]?v:v=(!v))  # print record and 0 or 1
a[v]--                                    # remove 0 or 1
}' file

另一种方法是:

  1. 创建具有正确比率的 0 和 1 序列:

    $ awk 'END{for(i=1;i<=FNR;++i) print (i <= 0.8*FNR) }' file
    
  2. 随机播放输出以将其随机化:

    $ awk 'END{for(i=1;i<=FNR;++i) print (i <= 0.8*FNR) }' file | shuf
    
  3. 将其粘贴到文件旁边,并用<逗号>字符作为分隔符:

    $ paste -d, file <(awk 'END{for(i=1;i<=FNR;++i) print (i <= 0.8*FNR) }' file | shuf)
    

我不想使用任何形式的随机数生成器的原因是,这可能会导致 100% 1 或 100% 零。或任何类似性质的东西。上面产生最接近的 80% 的 1 和 20% 的 0。

另一种方法是通过以下方式使用 awk 进行双重解析:

$ awk '(NR==FNR) { next }
(FNR==1) { for(i=1;i<NR;i++) a[i] = (i<0.8*(NR-1)) }
{ for(i in a) { print $0","a[i]; delete a[i]; break } }' file file

上面利用了for(i in a)以不确定的方式在数组中循环的事实。您可以通过快速执行来查看此内容

$ awk 'BEGIN{ORS=","; for(i=1;i<=20;++i) a[i]; for(i in a) print i; printf "n"}'
17,4,18,5,19,6,7,8,9,10,20,11,12,13,14,1,15,2,16,3,

但这依赖于实现。

最后,您实际上可以在awk中使用shuf来获得所需的结果

$ awk '(NR==FNR) { next }
(FNR==1) { cmd = "shuf -i 1-"(NR-1)" }
{ cmd | getline i; print $0","(i <= 0.8*(NR-FNR)) }' file file

这似乎更像是算法的问题,而不是编程的问题。您在问题中指出:我需要有 20% 的行带有 0,80% 的行带有 1s。所以第一个问题是,如果行数不是 5 的倍数,该怎么办。如果您总共有 112 行,则 20% 将是 22.4 行,这没有意义。

假设您可以重新定义任务来处理这种情况,最简单的解决方案是为前 20% 的行分配 0,为其余行分配 1。

但是假设您希望在 0 和 1 的分布中具有一定的随机性。一个快速而肮脏的解决方案是创建一个由您要兑换的零和一的数量组成的数组,并在每次迭代中从该数组中随机获取一个元素(并将其从数组中删除(。

添加到之前的回复中,这里有一个 Python 3 的方法可以做到这一点:

#!/usr/local/bin/python3
import csv
import math
import random
totalOflines = len(open('columns.csv').readlines())
newColumn = ( [0] * math.ceil(totalOflines * 0.20) ) + ( [1] * math.ceil(totalOflines * 0.80) )
random.shuffle(newColumn)
csvr = csv.reader(open('columns.csv'), delimiter = ",")
i=0
for row in csvr:
print("{},{},{},{}".format(row[0],row[1],row[2],newColumn[i]))
i+=1

问候!

最新更新