如何将csv文件中除“0”和前两个字段之外的所有文本转换为1?

如何将csv文件中除“0”和前两个字段之外的所有文本转换为1?

我有几个大的 .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- 如果字段i0,则不执行任何操作 ( 1),否则将字段设置i1
  • 1-awk将 1 解释为 true 并默认打印记录

正如 @EdMorton 的评论中所建议的,使用$1=($i!=0)is 是一个更短的替代方案$i==0?1:$i=1

  • $i!=0是一个逻辑测试,如果字段i不是 0awk将返回1fortrue0forfalse并相应地覆盖字段值

答案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 下一个参数是要运行的脚本。

剧本:

  • perlsplice()删除数组的一部分,并将该部分返回给调用者...因此splice @F,2删除并返回除数组的前两个元素之外的所有元素@F。实际上,splice可以做的事情远不止这些,但这就是我在这里使用它的全部目的。perldoc -f splice详情请参阅。

  • perl 的map函数将表达式应用于数组(列表)的每个元素。在这种情况下,列表是 splice 函数返回的元素。如果元素与正则表达式匹配,则map此处使用的表达式返回;如果不匹配,则返回 1。 返回一个数组,该数组被分配给数组变量。详情请参阅。0/^0$/map@outperldoc -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强制到/ 。+(…)(…).Int01

[在上面的代码示例中,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

注意:上面的代码会将空白值、000x0-00.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 答案将空白值、000x0-00.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也不是空字符串。零的其他表示形式,例如000x0-0 0.0zero被视为真的并更改为1.

相关内容