我有以下数据框,该数据框在水平和垂直方向上无限延续,仅在奇数列中带有负数:
-1 2 3 4 -5 9
2 3 -4 5 -6 11
我想要第二、第四和第六完整列(或每个偶数列),而减号仅来自第一、第三和第五(或每个奇数列),所以我得到这个:
- 2 4 - 9
3 - 5 - 11
最终得到这样的结果:
-2 4 -9
3 -5 -11
因此,我需要偶数列中的值保持不变,而奇数列中的值,如果有负值,则仅保留 - ,如果有正值,则丢弃它。
有没有办法用 awk/sed 来做到这一点?
据我所知,这大约是:
awk '{ for (i=2;i<=NF;i+=2) $i="" }1' FILE.txt | sed 's/[0-9,.]*//g'
答案1
道路sed
:
sed -E '
s/^(([ \t]*-?[ \t]*[0-9.]+[ \t]+[0-9.]+)*)[ \t]+-?[ \t]*[0-9.]+$/\1/;
s/[0-9.]+[ \t]+([0-9.]+)/\1/g'
输出:
-2 4 -9
3 -5 -11
如果列数为奇数,第一个表达式将删除尾随列。它通过查找 0 个或更多对来实现这一点<number> <number>
,其中第一个数字可以为负数。
编辑:sed
受@mikeserv启发的更短的解决方案:
sed -E '
s/[0-9.]+[ \t]*([0-9.]*)/\1/g;
s/[- \t]*$//'
同样的事情perl
:
perl -lpe 's/^((\s*-?\s*[\d.]+\s*[\d.]+)*)\s+-?\s*[\d.]+$/$1/o; s/[\d.]+\s+([\d.]+)/$1/g'
另一种方式perl
(可能是最干净的方式):
perl -lpe '$a = 1; s/([\d.]+\s*)/$a++ % 2 ? "" : $1/eg; s/[-\s]*$//o'
答案2
一个perl
一:
$ perl -anle 'BEGIN{$,=" "}
print map{$_=$F[$_]=~/^-/?"-$F[$_+1]":" $F[$_+1]"}grep{!($_%2)}0..$#F' file
-2 4 -9
3 -5 -11
-an
将输入拆分为@F
数组BEGIN{$,=" "}
将输出字段分隔符设置为空格grep{!($_%2)}0..$#F
获取数组中所有偶数索引@F
,即奇数元素的索引map{$_=$F[$_]=~/^-/?"-$F[$_+1]":" $F[$_+1]"}
检查奇数元素是否以 开头-
,然后附加-
到下一个偶数元素,否则附加一个空格
答案3
正如@terdon 的回答但没有 sed:
awk '{ for(i=1;i<=NF;i+=2){
if ($i<0) $(i+1)*=-1;
$i = "";
}
print
}'
答案4
这是一种方法:
$ awk '{for(i=1;i<=NF;i+=2){if($i<0){$i="-"}else{$i="";} }};1' file |
sed 's/- */-/g; s/ */ /g'
-2 4 -9
3 -5 -11
该awk
脚本会遍历所有奇数列,-
如果它们为负数,则将其值设置为 ,如果不是,则将其设置为空。然后,sed
删除 a 后面的所有空格-
,然后将多个连续空格替换为一个空格。请注意,这意味着对齐方式将被破坏,因为某些字段将包含两个或更多字符,而其他字段将包含一个字符。如果您正在使用字段,那不会是问题,它们只是看起来不漂亮。