我有几个大的 .csv 文件,我想将它们转换为二进制(1 和 0)格式。其中,除前两个字段外,所有包含文本的单元格都将变为 1,0 将保持为 0。
head Test.csv
Iss1,1,0,0,Hsapiens-I34,0,0,0,Mmusculus-H01,0,0
Iss1,11,0,Scerevisiae-U09,Hsapiens-I05,0,0,0,0,0,0
Iss1,21,0,0,Hsapiens-I05,0,0,0,Hsapiens-I31,0,0
Iss1,31,0,0,Mmusculus-H13,0,0,0,0,0,Hsapiens-I31
Iss1,41,0,Scerevisiae-U09,0,0,0,0,0,0,Hsapiens-I21
Iss1,51,0,0,0,0,0,0,Scerevisiae-U25,0,Hsapiens-I21
Iss1,61,0,0,Hsapiens-I34,0,0,0,Mmusculus-H13,0,0
预期结果是
head Test.csv
Iss1,1,0,0,1,0,0,0,1,0,0
Iss1,11,0,1,1,0,0,0,0,0,0
Iss1,21,0,0,1,0,0,0,1,0,0
Iss1,31,0,0,1,0,0,0,0,0,1
Iss1,41,0,1,0,0,0,0,0,0,1
Iss1,51,0,0,0,0,0,0,1,0,1
Iss1,61,0,0,1,0,0,0,1,0,0
其中文件中的所有文本都转换为 1。
如果有人能给我一些关于如何克服这个问题的建议,我将不胜感激。
谢谢
答案1
和awk
你一起可以做:
awk 'BEGIN {FS=OFS=","} {for (i=3;i<=NF;i++) {$i==0?1:$i=1}} 1' test.csv
BEGIN {FS=OFS=","}
- 将输入和输出分隔符设置为逗号for (i=3;i<=NF;i++)
- 我们将循环字段 3 直到最大字段数NF
$i==0?1:$i=1
- 如果字段i
为0
,则不执行任何操作 (1
),否则将字段设置i
为1
1
-awk
将 1 解释为 true 并默认打印记录
正如 @EdMorton 的评论中所建议的,使用$1=($i!=0)
is 是一个更短的替代方案$i==0?1:$i=1
$i!=0
是一个逻辑测试,如果字段i
是不是0
。awk
将返回1
fortrue
和0
forfalse
并相应地覆盖字段值
答案2
使用 Perl:
$ perl -F, -lne 'my @out = map { /^0$/ ? 0 : 1 } splice @F,2;
unshift @out, @F;
print join(",",@out)' Test.csv
Iss1,1,0,0,1,0,0,0,1,0,0
Iss1,11,0,1,1,0,0,0,0,0,0
Iss1,21,0,0,1,0,0,0,1,0,0
Iss1,31,0,0,1,0,0,0,0,0,1
Iss1,41,0,1,0,0,0,0,0,0,1
Iss1,51,0,0,0,0,0,0,1,0,1
Iss1,61,0,0,1,0,0,0,1,0,0
Zed227,28897871,0,0,1,0,0,0,1,0,0
Zed227,28897881,0,1,1,0,0,0,0,0,0
Zed227,28897891,0,0,1,0,0,0,1,0,0
Zed227,28897901,0,0,0,0,0,0,0,0,1
lad1,1,0,1,0,0,0,0,0,0,1
lad1,11,0,1,0,0,0,0,1,0,0
lad1,21,0,0,1,0,0,0,0,0,0
怎么运行的:
Perl 命令行选项:
- 该
-F,
选项告诉 perl 使用逗号作为字段分隔符。-F
还触发每个输入行的自动拆分,字段进入名为的数组@F
- 这类似于 awk 自动拆分字段为 $1、$2、$3 等。 -l
告诉 perl 自动处理行结束符,例如从输入中删除换行符并将它们添加回 的输出中print
。-n
使 perl 运行类似于sed -n
- 即读取并处理每一行,但仅打印明确告知的内容。-e
告诉 perl 下一个参数是要运行的脚本。
剧本:
perl
splice()
删除数组的一部分,并将该部分返回给调用者...因此splice @F,2
删除并返回除数组的前两个元素之外的所有元素@F
。实际上,splice
可以做的事情远不止这些,但这就是我在这里使用它的全部目的。perldoc -f splice
详情请参阅。perl 的
map
函数将表达式应用于数组(列表)的每个元素。在这种情况下,列表是 splice 函数返回的元素。如果元素与正则表达式匹配,则map
此处使用的表达式返回;如果不匹配,则返回 1。 返回一个数组,该数组被分配给数组变量。详情请参阅。0
/^0$/
map
@out
perldoc -f map
顺便说一句,我可以使用
eq
带有三元运算符(即 )的字符串相等比较( )$_ eq "0" ? 0 : 1
而不是正则表达式。字符串比较会比像这样的简单正则表达式更快,但除非您的 .csv 文件很大(数千行),否则不会明显如此。即便如此,最好还是使用eq
——我使用的唯一原因/^0$/
是它是我首先想到的。数字比较(
==
, ie$_ == 0 ? 0 : 1
)无法满足您的需求,因为不以数字开头的字符串(忽略任何前导空格)将计算为 0,而您需要将它们变为 1。unshift
与此相反shift
- 它将元素添加到数组的开头。在本例中,它将数组的剩余内容@F
(即未被 删除的前两个元素splice
)添加到数组的开头@out
。看perldoc -f unshift
。最后,该
@out
数组用逗号连接并打印。有关join
所使用函数的详细信息,请参见perldoc -f join
。
这可以简化为只有一个语句:
perl -F, -lne 'print join ",", @F[0..1], map { /^0$/ ? 0 : 1 } splice @F,2' Test.csv
不需要@out
作为临时变量,也不需要unshift
。
它的工作原理完全相同,但更难理解,特别是对于不熟悉 perl 的人......你必须从后到前阅读它,这样你才能知道每个函数正在获得什么输入。
答案3
使用乐(以前称为 Perl_6)
~$ raku -ne 'my @a = .split(","); \
@a[2..*] = do for @a[2..*] { $_ ~~ 0.Int ?? 0 !! 1 }; \
@a.join(",").put ;' file
或者:
~$ raku -ne 'my @a = .split(","); \
@a[2..*] .= map: { $_ ~~ 0.Int ?? 0 !! 1 }; \
@a.join(",").put ;' file
或者:
~$ raku -ne 'my @a = .split(","); \
@a[2..*] .= map: { +( $_ !~~ 0.Int ) }; \
@a.join(",").put;' file
Raku 是 Perl 编程语言家族中的一种编程语言。它具有对并发、异步和并行 (CAP) 的内置高级支持。
上面的前两个代码示例与 @cas 发布的优秀 Perl 答案非常相似。值得注意的是,Raku 有一个不对称的~~
“智能匹配”运算符,它是 Raku.ACCEPTS()
方法的语法糖(即“RHS 接受 LHS 吗?”)。许多与“类型”相关的问题可以使用 Raku 的~~
“智能匹配”运算符来解决。
Raku 还为内置三元运算符提供了新格式:(测试)??
真的 !!
错误的。有些人发现这个三元运算符更容易阅读。或者,第三个示例使用~~
在比较后设置返回变量的事实,允许将结果True
/值通过或False
强制到/ 。+(…)
(…).Int
0
1
[在上面的代码示例中,RHS.Int
实际上是多余的,并且智能匹配在没有任何额外强制的情况下工作得很好。但是,如果您需要任何默认值都未解决的特定比较,您可以执行 LHS/RHS 强制转换(通过.Str
、.Int
、等)。.Bool
输入示例:
Iss1,1,0,0,Hsapiens-I34,0,0,0,Mmusculus-H01,0,0
Iss1,11,0,Scerevisiae-U09,Hsapiens-I05,0,0,0,0,0,0
Iss1,21,0,0,Hsapiens-I05,0,0,0,Hsapiens-I31,0,0
Iss1,31,0,0,Mmusculus-H13,0,0,0,0,0,Hsapiens-I31
Iss1,41,0,Scerevisiae-U09,0,0,0,0,0,0,Hsapiens-I21
Iss1,51,0,0,0,0,0,0,Scerevisiae-U25,0,Hsapiens-I21
Iss1,61,0,0,Hsapiens-I34,0,0,0,Mmusculus-H13,0,0
示例输出:
Iss1,1,0,0,1,0,0,0,1,0,0
Iss1,11,0,1,1,0,0,0,0,0,0
Iss1,21,0,0,1,0,0,0,1,0,0
Iss1,31,0,0,1,0,0,0,0,0,1
Iss1,41,0,1,0,0,0,0,0,0,1
Iss1,51,0,0,0,0,0,0,1,0,1
Iss1,61,0,0,1,0,0,0,1,0,0
注意:上面的代码会将空白值、00
、0x0
、-0
和0.0
全部转换为零0
。特别是对于空白(空)值,您应该验证所有列是否都填充了 code raku -ne '.split(",", :skip-empty).elems.say;'
,带/不带:skip-empty
参数以检测差异。对于 Perl 粉丝,请参阅 Raku 文档部分“0”为真。
00
如果您想对空白值、 、0x0
、-0
和进行不同的处理0.0
,请查看 Perl 答案。@StéphaneChazelas 的 Perl 答案包括如何处理这些值(和空白)的解释。在我手中,@cas 的 Perl 答案将空白值、00
、0x0
、-0
和0.0
全部更改为1
(空白值的处理方式与 @StéphaneChazelas Perl 答案不同)。所以您可以选择(两种语言,三种治疗方法)!
https://docs.raku.org/language/operators#infix_??_!!
https://docs.raku.org/routine/ACCEPTS
https://docs.raku.org/routine/~~
https://raku.org
答案4
另一种perl
方法,@F
就地修改:
$ perl -F, -le 'map {$_ &&= 1} @F[2..$#F]; print join ",", @F' test.csv
Iss1,1,0,0,1,0,0,0,1,0,0
Iss1,11,0,1,1,0,0,0,0,0,0
Iss1,21,0,0,1,0,0,0,1,0,0
Iss1,31,0,0,1,0,0,0,0,0,1
Iss1,41,0,1,0,0,0,0,0,0,1
Iss1,51,0,0,0,0,0,0,1,0,1
Iss1,61,0,0,1,0,0,0,1,0,0
如果元素被1
视为真的,也就是说,如果它们既不是0
也不是空字符串。零的其他表示形式,例如00
、0x0
、-0
0.0
或zero
被视为真的并更改为1
.