使用shell删除特定列中包含特定字符串的50%行
我有一个数据文件,它有许多行和列。我想随机删除第二列中包含数字“2”的50%的行。如何在shell脚本中执行此操作?我的文件如下所示:使用shell删除特定列中包含特定字符串的50%行,shell,Shell,我有一个数据文件,它有许多行和列。我想随机删除第二列中包含数字“2”的50%的行。如何在shell脚本中执行此操作?我的文件如下所示: 264 2 -1.2000000000000000e+00 7.0825130926872939e+00 9.5601084339752944e+00 7.2651799153974066e+00 245 4 2.3999999999999999e+00 3.2314933672268427e+00 8.1060222635488888e+00 4.93009
264 2 -1.2000000000000000e+00 7.0825130926872939e+00 9.5601084339752944e+00 7.2651799153974066e+00
245 4 2.3999999999999999e+00 3.2314933672268427e+00 8.1060222635488888e+00 4.9300995049182887e+00
602 2 2.3999999999999999e+00 7.9943142143951045e+00 8.9555257846190486e+00 6.1149829552712900e+00
323 3 -1.2000000000000000e+00 9.5688081384508621e+00 1.0611671606914694e+01 8.9952891594417164e+00
45 3 -1.2000000000000000e+00 9.4185463105240714e+00 9.3227605688201560e+00 6.6654941991009027e+00
103 2 2.3999999999999999e+00 1.0178713184773681e+01 1.0522860587449216e+01 7.5396990175229996e+00
462 2 2.3999999999999999e+00 4.2166316392533885e+00 6.9152554630316221e+00 7.5523911902369765e+00
239 3 -1.2000000000000000e+00 7.8204053112970211e+00 8.2536094294868985e+00 9.4685060963111152e+00
598 3 -1.2000000000000000e+00 7.9895230606907504e+00 7.3376809962958367e+00 6.1930783591087541e+00
假设您的数据位于名为
input
的文件中:
awk 'BEGIN{srand()} $2!=2 || int(2*rand()) {print}' input
说明:
这将为随机数生成器选择一个随机种子。这在开始{srand()}
开始循环文件的每一行之前完成一次awk
在2美元=2 | | int(2*rand()){print}
中,这是一个带有条件的语句。语句是awk
,它将打印当前行的全部内容,但仅当条件为真时。条件有两个部分,它们是or-d。如果第二列的值不是2:print
。或者,如果$2,则该条件为true=2
的计算结果为非零(随机发生的概率为50-50),则为真 因此,第二列等于2的行中大约有一半被随机删除int(2*rand())
$2==2
行,他就不会保留50%,就像掷一枚公平的硬币不会给出50%的正面一样。假设受影响的行数为偶数,则以下代码将补偿该保留,精确到50%:
awk 'BEGIN{srand()}
NR==FNR && $2==2 {a[j++]=rand()}
NR==FNR {next}
FNR==1 {n=asort(a,b); cutoff=b[n/2]}
$2!=2 {print; next}
a[i++]<=cutoff {print}' input input
awk'开始{srand()}
NR==FNR&$2==2{a[j++]=rand()}
NR==FNR{next}
FNR==1{n=asort(a,b);cutoff=b[n/2]}
$2!=2{打印;下一个}
a[i++]为了保证约50%的删除,我们需要(提前)知道存储“2”值的许多“行”是谁
awk 'function get_random(total) {
while(1){
a = sprintf(int(total * rand()))
if (!( a in b )){
b[a]++
if (++i>int(total/2))
break
}
}
}
BEGIN{srand();k=0}
NR==FNR{if ($2==2){total++};next}
!i{get_random(total)}
$2!=2{print;next}
(sprintf(++k) in b){print}' inputfile inputfile
get_random
:将获取受影响记录的数量作为参数,而while循环将在关联数组(b)中存储总计/2(随机和非重复)值(使用转换为字符串)
NR==FNR{if($2==2){total++};next}
:第二个字段等于2的出现次数
!i{get_random(total)}
:当counteri
为空并且我们在文件的第二次迭代时调用函数
2美元=2{print;next}
:打印未受影响的行
(b中的sprintf(++k){print}
:仅当计数器在数组中时才打印筛选行(用随机值填充)。您可以使用perl、sed或awk吗?或者你真的只限于bash/shell吗?我可以使用awk或sed。也许我误解了,但是使用这种策略不能保证删除50%。您可以不使用“2”行结束(或全部使用)。我同意@klashxx。它给我随机的结果,有时我没有2,有时我有50%,有时我有超过50%。我该如何解决这个问题?@Rafat在你的问题中,据说有“许多行”。如果有“许多行”,那么这个算法将给你将近一半的删除。一枚普通硬币的抛投行为类似:经过多次抛投后,不太可能正好有一半是正面,但接近一半是正面。如果你只需要一半,那么也许克拉什的方法对你更合适。@Rafat我刚刚添加了一个版本,正好能达到50%。@John1024非常感谢你。我制作了一个测试文件,并试图在上面实现你的脚本,但奇怪的是,有时它会给我50%的删除率,有时它会给我更少的删除率。应该是这样吗?