从文本文件中删除每个单独列中的重复项
Posted
技术标签:
【中文标题】从文本文件中删除每个单独列中的重复项【英文标题】:Remove duplicates in each individual column from a text file 【发布时间】:2021-11-07 07:13:07 【问题描述】:我有一个包含 7 个制表符分隔列的文本文件。每列都有不同数量的行,其中的值可以重复。我想删除重复项,以便每列只有该特定列的唯一值。举个例子:
输入
C1 C2 C3 C4 C5 C6 C7
111 111 222 333 111 222 777
222 111 333 333 222 333 666
222 111 444 111 333 555 555
333 444 555 222 444 666 444
444 666 555 777 555 666 333
444 777 777 555 666 888 333
777 888 999 666 888
999
输出
C1 C2 C3 C4 C5 C6 C7
111 111 222 333 111 222 777
222 444 333 111 222 333 666
333 666 444 222 333 555 555
444 777 555 777 444 666 444
777 888 777 555 555 888 333
999 999 666 666
888
我想我需要使用 awk 来打印每一列并单独使用 sort -u,然后将这些输出粘贴在一起。那么,有没有一种方法可以创建一个循环,对于文本文件中的 i 列,将打印每一列 | sort - u,然后将它们全部粘贴在一起?
提前致谢, 卡洛斯
【问题讨论】:
转置,然后在行中制作唯一编号,然后再次转置。paste <(awk '!seen[$1]++ && $0=$1' file) <(awk '!seen[$2]++ && $0=$2' file) <(awk '!seen[$3]++ && $0=$3' file) <(awk '!seen[$4]++ && $0=$4' file) <(awk '!seen[$5]++ && $0=$5' file) <(awk '!seen[$6]++ && $0=$6' file) <(awk '!seen[$7]++ && $0=$7' file)
;-)
谢谢@Cyrus,它适用于这个特定的文件。但是,将来我将使用具有随机列数的文件来执行此操作。有没有办法做类似的事情: for i columns do paste
@Carlos Cyrus 是在开玩笑,请注意他们评论末尾的笑脸表情符号。
@EdMorton:是的,我已经看到灾难即将到来,它不会停留在 7 列。
【参考方案1】:
使用perl
代替它支持真正的多维数组:
perl -lane '
for my $n (0..$#F)
if (!exists $$vals[$n]$F[$n])
push @$cols[$n], $F[$n];
$$vals[$n]$F[$n] = 1;
END
for (1..$.)
my @row;
for my $n (0..$#cols)
push @row, shift @$cols[$n];
print join("\t", @row);
' input.txt
【讨论】:
【参考方案2】:在每个 Unix 机器上的任何 shell 中使用任何 awk:
$ cat tst.awk
BEGIN FS=OFS="\t"
for (colNr=1; colNr<=NF; colNr++)
val = $colNr
if ( !seen[colNr,val]++ )
rowNr = ++colRowNrs[colNr]
vals[rowNr,colNr] = val
numRows = (rowNr > numRows ? rowNr : numRows)
numCols = (NF > numCols ? NF : numCols)
END
for (rowNr=1; rowNr<=numRows; rowNr++)
for (colNr=1; colNr<=numCols; colNr++)
val = vals[rowNr,colNr]
printf "%s%s", val, (colNr<numCols ? OFS : ORS)
$ awk -f tst.awk file
C1 C2 C3 C4 C5 C6 C7
111 111 222 333 111 222 777
222 444 333 111 222 333 666
333 666 444 222 333 555 555
444 777 555 777 444 666 444
777 888 777 555 555 888 333
999 999 666 666
888
【讨论】:
【参考方案3】:假设
整个输出结果的 (awk
) 数组将适合内存
列数和行数可变
一个想法由一个(稀疏的)二维值数组组成,其中数组结构如下所示:
values[<column#>][<row#>]=<unique_cell_value>
使用单个 awk
调用的一个想法 a) 需要单次通过输入文件,而 b) 不需要任何转置/粘贴(以防万一任何人都认真对待赛勒斯的评论/建议):
awk '
BEGIN FS=OFS="\t"
maxNF = (NF > maxNF ? NF : maxNF) # keep track of max number of columns
for (i=1; i<=NF; i++)
if ( $i == "" ) # ignore empty cell
continue
for (j=1; j<=ndx[i]; j++) # loop through values already seen for this column
if ( $i == vals[i][j] ) # and if already seen then
$i = "" # clear the current cell and
break # break out of this for/testing loop
if ( $i != "" ) # if we got this var and the cell is not empty then
vals[i][++ndx[i]] = $i # store the new value in our array
END for (j=1; j<=NR; j++) # loop through all possible rows
pfx = ""
for (i=1; i<=maxNF; i++) # loop through all possible columns
printf "%s%s", pfx, vals[i][j] # non-existent array entries default to ""
pfx = OFS
printf "\n"
' input_file
注意:数组结构(arr[i][j]
)需要GNU awk
,否则我们可以转换为arr[i,j]
的伪双索引数组结构
这会生成:
C1 C2 C3 C4 C5 C6 C7
111 111 222 333 111 222 777
222 444 333 111 222 333 666
333 666 444 222 333 555 555
444 777 555 777 444 666 444
777 888 777 555 555 888 333
999 999 666 666
888
【讨论】:
以上是关于从文本文件中删除每个单独列中的重复项的主要内容,如果未能解决你的问题,请参考以下文章
从 Pandas Dataframe Column 中删除重复的逗号,换句话说,我只需要列中的文本,用逗号分隔它们