根据不同的 gsub 条件同时 gsub 多列?
gsub many columns simultaneously based on different gsub conditions?
我有一个包含以下数据的文件-
输入-
A B C D E F
A B B B B B
C A C D E F
A B D E F A
A A A A A F
A B C B B B
如果从第 2 行开始的任何其他行与第 1 行具有相同的字母,则应将它们更改为 1。基本上,我试图找出任何行与第一行的相似程度.
期望的输出-
1 1 1 1 1 1
1 1 B B B B
C A 1 1 1 1
1 1 D E F A
1 A A A A 1
1 1 1 B B B
第一行全为 1,因为它与自身相同(很明显)。在第二行中,第一列和第二列与第一行相同 (A B
),因此它们变为 1 1
。其他行依此类推。
我写了下面的代码来做这个转换-
for seq in {1..1} ; #Iterate over the rows (in this case just row 1)
do
for position in {1..6} ; #Iterate over the columns
do
#Define the letter in the first row with which I'm comparing the rest of the rows
aa=$(awk -v pos=$position -v line=$seq 'NR == line {print $pos}' f)
#If it matches, gsub it to 1
awk -v var=$aa -v pos=$position '{gsub (var, "1", $pos)} 1' f > temp
#Save this intermediate file and now act on this
mv temp f
done
done
如您所想,这真的很慢,因为嵌套循环很昂贵。我的真实数据是一个 60x10000 矩阵,这个程序需要大约 2 个小时才能 运行。
我希望你能帮助我摆脱内部循环,这样我就可以一步完成所有 6 个 gsub。也许将它们放在自己的数组中?我的 awk
技能还不是很好。
您可以使用这个更简单的 awk 命令来完成工作,因为我们避免了 shell 中的嵌套循环并在嵌套循环中重复调用 awk:
awk '{for (i=1; i<=NF; i++) {if (NR==1) a[i]=$i; if (a[i]==$i) $i=1} } 1' file
1 1 1 1 1 1
1 1 B B B B
C A 1 1 1 1
1 1 D E F A
1 A A A A 1
1 1 1 B B B
编辑:
根据下面的评论,您可以执行以下操作来获取每一行中每一列的总和:
awk '{sum=0; for (i=1; i<=NF; i++) { if (NR==1) a[i]=$i; if (a[i]==$i) $i=1; sum+=$i}
print [=11=], sum}' file
1 1 1 1 1 1 6
1 1 B B B B 2
C A 1 1 1 1 4
1 1 D E F A 2
1 A A A A 1 2
1 1 1 B B B 3
输入
$ cat f
A B C D E F
A B B B B B
C A C D E F
A B D E F A
A A A A A F
A B C B B B
想要o/p
$ awk 'FNR==1{split([=11=],a)}{for(i=1;i<=NF;i++)if (a[i]==$i) $i=1}1' f
1 1 1 1 1 1
1 1 B B B B
C A 1 1 1 1
1 1 D E F A
1 A A A A 1
1 1 1 B B B
解释
FNR==1{ .. }
当awk
读取当前文件的第一条记录时,在大括号内进行操作
split(string, array [, fieldsep [, seps ] ])
Divide string into pieces separated by fieldsep and store the pieces
in array and the separator strings in the seps array.
split([=16=],a)
split current record or row ([=17=]
) into pieces by fieldsep (defualt space, as
we have not supplied 3rd argument) and store the pieces in array a
So array a
contains data from first row
a[1] = A
a[2] = B
a[3] = C
a[4] = D
a[5] = E
a[6] = F
for(i=1;i<=NF;i++)
Loop through all the fields of for each record of file till end of file.
if (a[i]==$i) $i=1
if first row's column value of current index (i
) is equal to
current column value of current row set current column value = 1 ( meaning modify current column value )
现在我们修改了列值,接下来只是打印修改后的行
}1
1
始终计算为真,它执行默认操作 {print [=25=]}
评论更新请求
Same question here, I have a second part of the program that adds up
the numbers in the rows. I.e. You would get 6, 2, 4, 2, 2, 3 for this
output. Can your program be tweaked to get these values out at this
step itself?
$ awk 'FNR==1{split([=13=],a)}{s=0;for(i=1;i<=NF;i++)if(a[i]==$i)s+=$i=1;print [=13=],s}' f
1 1 1 1 1 1 6
1 1 B B B B 2
C A 1 1 1 1 4
1 1 D E F A 2
1 A A A A 1 2
1 1 1 B B B 3
我有一个包含以下数据的文件-
输入-
A B C D E F
A B B B B B
C A C D E F
A B D E F A
A A A A A F
A B C B B B
如果从第 2 行开始的任何其他行与第 1 行具有相同的字母,则应将它们更改为 1。基本上,我试图找出任何行与第一行的相似程度.
期望的输出-
1 1 1 1 1 1
1 1 B B B B
C A 1 1 1 1
1 1 D E F A
1 A A A A 1
1 1 1 B B B
第一行全为 1,因为它与自身相同(很明显)。在第二行中,第一列和第二列与第一行相同 (A B
),因此它们变为 1 1
。其他行依此类推。
我写了下面的代码来做这个转换-
for seq in {1..1} ; #Iterate over the rows (in this case just row 1)
do
for position in {1..6} ; #Iterate over the columns
do
#Define the letter in the first row with which I'm comparing the rest of the rows
aa=$(awk -v pos=$position -v line=$seq 'NR == line {print $pos}' f)
#If it matches, gsub it to 1
awk -v var=$aa -v pos=$position '{gsub (var, "1", $pos)} 1' f > temp
#Save this intermediate file and now act on this
mv temp f
done
done
如您所想,这真的很慢,因为嵌套循环很昂贵。我的真实数据是一个 60x10000 矩阵,这个程序需要大约 2 个小时才能 运行。
我希望你能帮助我摆脱内部循环,这样我就可以一步完成所有 6 个 gsub。也许将它们放在自己的数组中?我的 awk
技能还不是很好。
您可以使用这个更简单的 awk 命令来完成工作,因为我们避免了 shell 中的嵌套循环并在嵌套循环中重复调用 awk:
awk '{for (i=1; i<=NF; i++) {if (NR==1) a[i]=$i; if (a[i]==$i) $i=1} } 1' file
1 1 1 1 1 1
1 1 B B B B
C A 1 1 1 1
1 1 D E F A
1 A A A A 1
1 1 1 B B B
编辑:
根据下面的评论,您可以执行以下操作来获取每一行中每一列的总和:
awk '{sum=0; for (i=1; i<=NF; i++) { if (NR==1) a[i]=$i; if (a[i]==$i) $i=1; sum+=$i}
print [=11=], sum}' file
1 1 1 1 1 1 6
1 1 B B B B 2
C A 1 1 1 1 4
1 1 D E F A 2
1 A A A A 1 2
1 1 1 B B B 3
输入
$ cat f
A B C D E F
A B B B B B
C A C D E F
A B D E F A
A A A A A F
A B C B B B
想要o/p
$ awk 'FNR==1{split([=11=],a)}{for(i=1;i<=NF;i++)if (a[i]==$i) $i=1}1' f
1 1 1 1 1 1
1 1 B B B B
C A 1 1 1 1
1 1 D E F A
1 A A A A 1
1 1 1 B B B
解释
FNR==1{ .. }
当awk
读取当前文件的第一条记录时,在大括号内进行操作
split(string, array [, fieldsep [, seps ] ])
Divide string into pieces separated by fieldsep and store the pieces in array and the separator strings in the seps array.
split([=16=],a)
split current record or row (
[=17=]
) into pieces by fieldsep (defualt space, as we have not supplied 3rd argument) and store the pieces in arraya
So arraya
contains data from first row
a[1] = A
a[2] = B
a[3] = C
a[4] = D
a[5] = E
a[6] = F
for(i=1;i<=NF;i++)
Loop through all the fields of for each record of file till end of file.
if (a[i]==$i) $i=1
if first row's column value of current index (
i
) is equal to current column value of current row set current column value = 1 ( meaning modify current column value )
现在我们修改了列值,接下来只是打印修改后的行
}1
1
始终计算为真,它执行默认操作{print [=25=]}
评论更新请求
Same question here, I have a second part of the program that adds up the numbers in the rows. I.e. You would get 6, 2, 4, 2, 2, 3 for this output. Can your program be tweaked to get these values out at this step itself?
$ awk 'FNR==1{split([=13=],a)}{s=0;for(i=1;i<=NF;i++)if(a[i]==$i)s+=$i=1;print [=13=],s}' f
1 1 1 1 1 1 6
1 1 B B B B 2
C A 1 1 1 1 4
1 1 D E F A 2
1 A A A A 1 2
1 1 1 B B B 3