我有两个大的制表符分隔文件(>10GB),我知道当它们被排序时,它们的内容是相同的。
但是,当它们共享相同的“键”(此处的键被定义为基于Source
和Location
列分组的行)时,我对行的顺序和交换的索引感兴趣。
换句话说,这两个文件之间的行应该仅在它们来自同一组时(即它们共享相同的源和位置时)相互比较。
例如,在下面的示例中,第 4、5、6 行file1.tsv
应该与第 4、5、6行进行比较file2.tsv
注意:文件是普通的 TSV。此处仅添加额外的空格以使列居中和右对齐,以获得更好的可见性。这些空间不是原始文件的一部分
文件1.tsv
Identifier Position Source Location
AY1:2301 87 ch1 14
BC1U:4010 105 ch1 14
AC44:1230 90 ch1 15
AJC:93410 83 ch1 16
ABYY:0001 101 ch1 16
ABC:01 42 ch1 16
HH:A9CX 413 ch1 17
LK:9310 2 ch1 17
JFNE:3410 132 ch1 18
MKASDL:11 14 ch1 18
MKDFA:9401 18 ch1 18
MKASDL1:011 184 ch2 50
LKOC:AMC02 18 ch2 50
POI:1100 900 ch2 53
MCJE:09HA 11 ch2 53
ABYCI:1123 15 ch2 53
MNKA:410 1 ch2 53
文件2.tsv
Identifier Position Source Location
AY1:2301 87 ch1 14
BC1U:4010 105 ch1 14
AC44:1230 90 ch1 15
ABC:01 42 ch1 16
ABYY:0001 101 ch1 16
AJC:93410 83 ch1 16
HH:A9CX 413 ch1 17
LK:9310 2 ch1 17
MKASDL:11 14 ch1 18
JFNE:3410 132 ch1 18
MKDFA:9401 18 ch1 18
MKASDL1:011 184 ch2 50
LKOC:AMC02 18 ch2 50
MNKA:410 1 ch2 53
POI:1100 900 ch2 53
ABYCI:1123 15 ch2 53
MCJE:09HA 11 ch2 53
我想做类似于“差异”的事情,但在“组”级别(只有在它们共享相同Source
和时才比较行Location
)
当行的顺序在同一个“源/位置”“组”(或键)中“交换”时,我想提取原始的“行号” 。
整行应该在内容方面匹配。
但我不知道该怎么做。当我的原始数据集有数百万行时,我只能考虑编写一个效率极低的 for 循环。
预期结果:
Group_Source:Location df1.index df2.index
ch1:16 4 6
ch1:16 6 4
ch1:18 9 10
ch1:18 10 9
ch2:53 14 15
ch2:53 15 17
ch2:53 17 14
假设:
- 两个数据框的行数相同
- 两个数据帧是相同的(仅交换行的顺序,因此如果两者都按源排序,然后按位置排序,然后按位置排序,然后按标识符排序,那么它们将完全相同)
- “交换”的行总是在所有列的内容方面完全匹配
由于输入文件的大小,这是我可能会使用的罕见情况之一,
getline
因此我们一次只在内存中保存几行而不是> 10G:有关更多信息
getline
,请阅读http://awk.freeshell.org/AllAboutGetline。即使在输入中重复了
Identifier
和/或,上述内容也可以工作Position
,因为它正在比较两个文件之间的所有 4 个字段。它确实假设 Source 和 Location 值在两个文件之间的顺序相同,如示例输入中所示。这在
awk
. 例如:解释
if(FNR==1){ next }
:FNR
保存当前正在读取的文件的行号(记录号)。因此,如果这是任一输入文件的第一行,请跳过它,因为我们不想处理标题。else if(FNR==NR){ ... }
:NR
保存当前输入的行号,与正在读取的文件无关。因此,如果FNR
等于NR
,则意味着我们正在读取第一个文件。a[$1]=FNR-1
:所以,如果这是第一个文件,则将第一个字段作为索引(键)存储在关联数组中,其值将是当前文件的行号(FNR
),但减一,因为我们不想计算标题.else if ( a[$1] != FNR-1 ){
: 这个是和前一个相关联的,所以只有在不等于else if
时才会输入这个,所以只有在读取第二个文件的时候。因此,如果我们正在读取第二个文件并且该行的第一个字段存储在数组中的值不等于当前文件的行号减一,那么我们要打印。FNR
NR
a
print $3":"$4, FNR-1, a[$1]
:所以我们打印第 3 个字段、a:
和第 4 个字段,然后将 FNR 减一和存储在a
数组中的第一个字段的值。最后,要使用填充和标题漂亮地打印它,请使用:
重要提示:这种方法要求您在内存中为第一个文件的每一行(标题栏)保留少量数据。这可能是大文件的一个问题,尽管在您可能执行此类操作的大多数机器上可能不是。如果这是一个问题,我推荐Ed 的答案,它应该更快并且没有任何内存问题。