我一直在四处寻找,但我无法发现任何接近我所寻找的东西。 我想一个例子将首先解释它: 输入:
------------------------------------|
| List1 | List2 | List3 |
| 1 | 2 | 3 |
| 2 | 3 | 4 |
| 3 | 4 | 5 |
| 4 | 5 | 6 |
| 5 | 6 | 7 |
| 6 | 7 | a |
| 7 | 8 | b |
| a | d | c |
期望的输出:
------------------------------------|
| List1 | List2 | List3 |
| 1 | 8 | b |
| | d | c |
因此,正如您所看到的,目标是在每列中仅保留表中其他位置未找到的内容。理想情况下,这应适用于任意数量的列。
awk,bash甚至excel中的任何内容都没问题。 到目前为止,我已经玩过awk,但无济于事。
任何帮助将不胜感激。
谢谢大家。
编辑,澄清。 实际输入是我想要比较的不同列表。理想情况下,它们每个都在不同的文件中。现在,我可以轻松地将它们合并以匹配示例中给出的输入,以便答案中提供的代码适用。
答案 0 :(得分:1)
请尝试Python
:
import re
with open("./input_file", "rt") as file:
f = file.readlines()
cols = [re.findall(r"[\w']+", x) for x in f[3:]]
col1 = set(x[0] for x in cols)
col2 = set(x[1] for x in cols)
col3 = set(x[2] for x in cols)
print col1.difference(col2.union(col3))
print col2.difference(col1.union(col3))
print col3.difference(col1.union(col2))
输出:
set(['1'])
set(['8', 'd'])
set(['c', 'b'])
<强> 修改 强>
增强版本以匹配所需的格式。也应该使用任意数量的列/行。
from __future__ import print_function
import re
with open("./input_file", "rt") as file:
f = file.readlines()
for x in f[:2]:
print(x,end='')
rows = [re.findall(r"(?<=\|)(.*?)(?:\|)", x) for x in f[2:]]
col_num, col_width = len(rows[0]), len(rows[0][0])
cols = [ set(y[x] for y in rows) for x in range(len(rows[0]))]
uniq_cols = []
for col in cols:
uniq_cols.append(list(col.difference(set().union(*[c for c in cols if c != col]))))
for x in range(max(len(x) for x in uniq_cols)):
print('|', end='')
for col in uniq_cols:
try:
print(col[x], end='')
print('|', end='')
except IndexError:
print(' '*col_width, end='')
print('|', end='')
print('\n', end='')
输出:
------------------------------------|
| List1 | List2 | List3 |
| 1 | 8 | c |
| | d | b |
答案 1 :(得分:1)
$ cat tst.awk
BEGIN { FS="[[:space:]]*[|][[:space:]]*" }
NR<3 { print; next }
{
for (i=2;i<NF;i++) {
cnt[$i]++
inCells[NR,i] = $i
}
}
END {
for (inRowNr=3; inRowNr<=FNR; inRowNr++) {
for (colNr=2; colNr<NF; colNr++) {
val = inCells[inRowNr,colNr]
if ( cnt[val] == 1 ) {
outRowNr = ++colOutRowNr[colNr]
outCells[outRowNr,colNr] = val
numOutRows = (outRowNr > numOutRows ? outRowNr : numOutRows)
}
}
}
for (outRowNr=1; outRowNr<=numOutRows; outRowNr++) {
printf "|"
for (colNr=2; colNr<NF; colNr++) {
printf " %s |", ((outRowNr,colNr) in outCells ? outCells[outRowNr,colNr] : " ")
}
print ""
}
}
$ awk -f tst.awk file
------------------------------------|
| List1 | List2 | List3 |
| 1 | 8 | b |
| | d | c |
答案 2 :(得分:0)
awk
救援!
$ awk 'function abc(x,y,z)
{for(k in x) if(!(k in y || k in z)) printf "%s", FS k;
print ""
}
NR==1{split($0,h); next}
{a[$1];b[$2];c[$3]}
END{printf "%s", h[1]; abc(a,b,c);
printf "%s", h[2]; abc(b,a,c);
printf "%s", h[3]; abc(c,a,b) }' file
List1 1
List2 8 d
List3 b c
通常,由于记录的逻辑单元是行(行),因此列操作更难。您可以将其转换回列格式我不确定是否需要。
答案 3 :(得分:0)
您可以使用awk:
对此文件进行2次传递awk 'FNR==NR{for(i=1; i<=NF; i++) dup[$i]++; next}
{for(i=1; i<=NF; i++) printf "%s%s", ((dup[$i]>1)? ".":$i), OFS; print ""}' file file |
column -t
<强>输出:强>
List1 List2 List3
1 . .
. . .
. . .
. . .
. . .
. . .
. 8 b
. d c
使用DOT
只是为了在输出中显示空白列。
要正确使用格式:
awk 'FNR==NR{
for(i=1; i<=NF; i++)
dup[$i]++
fld=NF
next
} {
for(i=1; i<=NF; i++)
if (dup[$i]<=1) {
for(j=1; j<i; j++)
if (dup[$j]>1)
printf "\t"
p++
printf "%s%s", $i, (p && p%fld == 0) ? ORS : OFS
}
}
END{
print ""
}' file file
<强>输出:强>
List1 List2 List3
1 8 b
d c
答案 4 :(得分:0)
awk -F '\|'
{ l1[$2]++; l2[$3]++; l3[$4]++ }
END{
for i in l1 {if(l2[i] > 0 || l3[i] > 0 ) delete l1[i]}
for i in l2 {if(l1[i] > 0 || l3[i] > 0 ) delete l2[i]}
for i in l3 {if(l1[i] > 0 || l2[i] > 0 ) delete l3[i]}
# formatting report is left as an exercise
}
答案 5 :(得分:0)
对于您的示例格式相当严格,这里是一个shell脚本,除了第一行之外,它还会生成您期望的输出。基本上,您将数据读入单独的列,并记录表中的所有数据。然后生成没有重复数据的数据列。然后打印去重复数据。如果您有包含空格的数据,这将会中断,但要解决此问题,您只需调整$ IFS以匹配您的分隔符。
这也是ksh93特有的。我不知道在打击中会有多少打破,但我不喜欢打击那么多。我认为如果你想用另一种语言重新实现它应该是相当可读的,但是在某个地方的Linux或UNIX系统上安装ksh93可能更容易(RHEL和Ubuntu都附带一个包,就像大多数常见系统一样)。
#!/usr/bin/ksh
IFS="${IFS}|" # also split on pipe
[[ -f "${1:-}" ]] || { print -- "'${1:-}' is not a file" >&2; exit 1; }
exec 3<$1
read firstline <&3
read -A headings <&3
# prune first element, due to weird line splitting
headings=("${headings[@]:1}")
# build data structure of all columns and aggregated values
typeset -a data
typeset -A counter
typeset -i row=0
while read -A line
do
col=0
#for v in ${line[@]}
for v in "${line[@]:1}" # ignore false first element
do
data[$((col++))][$row]=$v
(( counter[$v] += 1 ))
done
((row++))
done <&3
# remove duplicated data from columns
row=0
typeset -i maxrows=0
typeset -a deduped
for col in {1..${#data[@]}}
do
c=$(( col - 1 ))
row=0
for v in ${data[$c][@]}
do
[[ ${counter[$v]} -eq 1 ]] && deduped[$c][$((row++))]=${v}
done
[[ $row -gt $maxrows ]] && maxrows=$row
done
# pad short columns with empty elements and calculate widths
typeset -a widths
for col in {1..${#deduped[@]}}
do
c=$((col-1))
widths[$c]=${#headings[$c]} # default to heading width
while [[ ${#deduped[$c][@]} -lt $maxrows ]]
do
deduped[$c][${#deduped[$c][@]}]=''
[[ ${widths[$c]} -lt ${#v} ]] && widths[$c]=${#v}
done
(( widths[$c] += 2 )) # allow for a space on each side
done
# print the table out
# header first
format='|'
for w in "${widths[@]}"
do
format="${format}%=${w}s|"
done
format="${format}\n"
printf "$format" "${headings[@]}"
# then table
row=0
while [[ $row -lt $maxrows ]]
do
typeset -a r
for col in {1..${#deduped[@]}}
do
c=$((col-1))
r[$c]="${deduped[$c][$row]}"
done
printf "$format" "${r[@]}"
((row++))
done
处理您的测试文件:
$ ./test.ksh testfile
| List1 | List2 | List3 |
| 1 | 8 | b |
| | d | c |
答案 6 :(得分:0)
此回复主要关注有n列的一般情况,对于任何n&gt; = 1.这使得程序比其他情况稍长。
由于OP引用了Excel,因此该响应还假定数据可用作具有分隔符分隔值的文件(例如TSV或管道分隔)。为了便于阅读,我将假设一个简单的CSV,如下所示:
List1,List2,List3
1,2,3
2,3,4
3,4,5
4,5,6
5,6,7
6,7,a
7,8,b
a,d,c
此响应还假设在输出中,每列中值的排序并不重要。 (另一种情况留给读者练习: - )
最后的假设是我们可以使用输入字段分隔符作为输出字段分隔符(OFS = FS)。
然后是awk程序,它不需要超过标准的awk:
awk -F, '
# print an m by n matrix, a:
function printm(a,m,n, i,j, row) {
for(i=1;i<=m;i++) {
row=a[i,1];
for(j=2;j<=n;j++) {row = row OFS a[i,j]}
print row;
}
}
function maxfields(i) { if (MAXFIELDS =="" || MAXFIELDS<i ) {MAXFIELDS=i; } }
function maxcounter(i) { if (MAXCOUNTER=="" || MAXCOUNTER<i) {MAXCOUNTER=i;} }
# process one line of input:
function assemble( i) {
for(i=1;i<=NF;i++) {
if ($i in seen) { delete col[$i] }
else { seen[$i]; col[$i]=i; }
}
}
function finish( i,x) {
for(i=1; i<=MAXFIELDS; i++) { counter[i]=1 }
for (x in col) { a[ counter[col[x]], col[x] ] = x;
maxcounter(counter[col[x]]++); }
printm(a, MAXCOUNTER, MAXFIELDS)
}
BEGIN {OFS=FS}
NR==1 {print; next;}
{maxfields(NF); assemble();}
END { finish(); } '
输出:
List1,List2,List3
1,d,b
,8,c