是否可以从第一列包含与其他文件名称匹配的标识符的文件中提取某些行?

Posted

技术标签:

【中文标题】是否可以从第一列包含与其他文件名称匹配的标识符的文件中提取某些行?【英文标题】:Is it possible to extract certain rows from a file where the first column contains an identifier that matches the names of other files? 【发布时间】:2021-12-23 08:31:39 【问题描述】:

我昨天在一个更大的问题中提出了这个问题。一位评论者建议将其拆分为自己的问题。

我有 x 个文件。前五个文件的内容如下所示。在第二行,i 是一个计数器,因此i = 0 用于第一个文件。 i 旁边是 time,当 i 增加 1 时,它总是增加 0.5。

但是,我只保留了每个第 x 个文件。因此,i = 0 用于我拥有的第一个文件,i = 100 用于第二个文件,依此类推。

6 # This file is called "0.xyz" (<--the "6" is the same in all files)
i =       0, time =        0.000, k =      9000000000000
X        -7.6415350292        6.0494971539        8.1919697993
Y        -6.6418362233        5.9231018862        8.4056822626
Y        -8.0518670684        6.3158684817        9.0061271154
X        26.8252967820       20.4661074967       17.8025744066
Y        26.4477411207       20.4071029058       16.9121571912
Y        26.4399648474       21.2950722068       18.1009273227

6 # This file is called "100.xyz"
i =       100, time =        50.000, k =      2500000000000
X        -6.2423192714       -1.5704681396       -9.5648670474
Y        -5.4925100813       -1.6522059045       -8.9030589772
Y        -6.7765278574       -2.3616512405       -9.4776648590
X         4.1248924594       27.8487302083      -17.5400886312
Y         4.1238657681       26.9869907778      -17.9727402579
Y         5.0750649402       28.1292768156      -17.6848507559

6 # This file is called "200.xyz"
i =       200, time =        100.000, k =      3945000000000
X        19.0090162215       -5.9338939011        6.1931167954
Y        18.4748060757       -6.4905073540        5.6656446036
Y        19.2825591449       -6.4479943255        7.0179774953
X        11.0203415273       34.6029396705        2.7220660957
Y        11.1184002007       34.8398120338        1.8089008500
Y        10.3349649622       33.9509485292        2.5605794622

6 # This file is called "300.xyz"
i =       300, time =       150.000, k =      2341000000000
X        -7.6415350292        6.0494971539        8.1919697993
Y        -6.6418362233        5.9231018862        8.4056822626
Y        -8.0518670684        6.3158684817        9.0061271154
X        26.8252967820       20.4661074967       17.8025744066
Y        26.4477411207       20.4071029058       16.9121571912
Y        26.4399648474       21.2950722068       18.1009273227

6 # This file is called "400.xyz"
i =       400, time =        200.000, k =      2500000000000
X        -6.2423192714       -1.5704681396       -9.5648670474
Y        -5.4925100813       -1.6522059045       -8.9030589772
Y        -6.7765278574       -2.3616512405       -9.4776648590
X         4.1248924594       27.8487302083      -17.5400886312
Y         4.1238657681       26.9869907778      -17.9727402579
Y         5.0750649402       28.1292768156      -17.6848507559

我想做的是将这些文件(上)与另一个文件(下)中的数据进行匹配。在下面的文件中,每一行根据i(步骤)匹配上面的一个文件。然后我想将下面文件中匹配行的前三列打印到一个新文件中。

虽然我已包含文件名,但我更愿意使用i 进行匹配,而不是文件名。

我知道如何通过简单的迭代来做到这一点。意思是,我可以计算下面文件的每 xth 行并将其打印到一个新文件中。但是,我想使用一种更复杂的方法来专门匹配i,因为这些文件很长,可能会丢失行,所以我最终会在上面的文件和我想要的文件之间出现不匹配。 .

    #   Step       Time        Ax                   Ay                  Az                  Bx                  By                  Bz                  Cx                  Cy                  Cz                     Final 
           0       0.000       14.8032123290        0.0000000000        0.0000000000        0.0000000000       14.8032123290        0.0000000000        0.0000000000        0.0000000000       14.8032123290          3243.9033438318
           1       0.500       14.8029498502        0.0000000000        0.0000000000        0.0000000000       14.8029498502        0.0000000000        0.0000000000        0.0000000000       14.8029498502          3243.7307919244
           2       1.000       14.8026923814        0.0000000000        0.0000000000        0.0000000000       14.8026923814        0.0000000000        0.0000000000        0.0000000000       14.8026923814          3243.5615395313
           3       1.500       14.8024398604        0.0000000000        0.0000000000        0.0000000000       14.8024398604        0.0000000000        0.0000000000        0.0000000000       14.8024398604          3243.3955453870
           4       2.000       14.8021922354        0.0000000000        0.0000000000        0.0000000000       14.8021922354        0.0000000000        0.0000000000        0.0000000000       14.8021922354          3243.2327751298
...
           100       50.000       14.8032123290        0.0000000000        0.0000000000        0.0000000000       14.8032123290        0.0000000000        0.0000000000        0.0000000000       14.8032123290          3243.9033438318
...
           200       100.000       14.8029498502        0.0000000000        0.0000000000        0.0000000000       14.8029498502        0.0000000000        0.0000000000        0.0000000000       14.8029498502          3243.7307919244
...
           300       150.000       14.8026923814        0.0000000000        0.0000000000        0.0000000000       14.8026923814        0.0000000000        0.0000000000        0.0000000000       14.8026923814          3243.5615395313
           301       150.500       14.8024398604        0.0000000000        0.0000000000        0.0000000000       14.8024398604        0.0000000000        0.0000000000        0.0000000000       14.8024398604          3243.3955453870
...
           400       200.000       14.8021922354        0.0000000000        0.0000000000        0.0000000000       14.8021922354        0.0000000000        0.0000000000        0.0000000000       14.8021922354          3243.2327751298

示例我希望通过操作上面的文件来匹配问题顶部的示例文件集的结果:

   0         0.000         14.8032123290        
   100       50.000        14.8032123290
   200       100.000       14.8029498502
   300       150.000       14.8026923814
   400       200.000       14.8021922354

如果有人对如何处理此问题有任何提示,我将不胜感激。

【问题讨论】:

请添加到您的问题(不发表评论):您搜索了什么,找到了什么?您尝试过什么,它是如何失败的? 每个问题都应该独立存在;这意味着样本输入应该与(预期的)输出匹配......这里不是这种情况;我建议更新问题以显示匹配数据(输入和输出之间);或者,由于我们知道前 6 个文件是从一个较大的文件中提取的(请参阅之前问答的链接),因此(相反)提供较大文件的样本和匹配的(预期的)输出可能更有意义;一旦您找到可接受的答案,您就可以将这两个问答整合到一个脚本中(以满足您最初的两部分要求) @markp-fuso,谢谢。我明白你的意思,希望问题现在更清楚了。 在某一时刻您声明 根据 i(步骤)和时间匹配 ...,然后您声明 使用 i 或时间进行匹配我>;您能否更新问题以阐明匹配要求...i *AND* timei *OR* time @markp-fuso,是的,如果我能找到引起混乱的地方,我会很高兴。我很偏执,所以更愿意匹配 i AND time,但只匹配 i 应该没问题。 【参考方案1】:

您可以使用awk 脚本执行此操作,如下所示:

awk 'FNR == 1 
if ($0 ~ /^i =/) 
    dataFile = 0;
    step[$3+0] = FILENAME;

else dataFile = 1;


dataFile == 1 && step[$1] 
    print $1, $2, $3;
' *.xyz data.txt

(假设最终文件名为data.txt;根据需要进行更改)

FNR == 1 匹配每个文件的第一行,并且将从xyz 文件中捕获步骤,或者设置一个标志,指示我们已经到达数据文件。由于请求执行数学运算,$3 + 0 位只会强制 awk 将第三个字段转换为数字(,删除尾随逗号)。

dataFile == 1 &amp;&amp; step[$i] 匹配数据文件中在xyz 文件中看到步长值的行。

注意:您必须在最终数据文件之前指定所有xyz文件,以便在处理数据文件之前收集所有步骤。


抱歉,当我将上述解决方案放在一起时,我认为 # This file is called 行不是文件的一部分。修改后的脚本在这里:

awk '
FNR == 2 && FILENAME != ARGV[ARGC-1] 
    step[$3+0] = FILENAME;


FILENAME == ARGV[ARGC-1] && step[$1] 
    print $1, $2, $3;
' *.xyz data.txt

此版本使用 ARGV & ARGC 来判断是否正在处理“数据”文件。

如果不是数据文件和行号== 2,缓存“step”值 如果数据文件和步骤在列表中,则打印前 3 个字段

结果:

0 0.000 14.8032123290
100 50.000 14.8032123290
200 100.000 14.8029498502
300 150.000 14.8026923814
400 200.000 14.8021922354

【讨论】:

非常感谢。在处理大型文件的操作方面,Awk 比 Python(我习惯使用它)快得多,但我没有足够的经验来做我有时需要做的事情。谢谢你教育我! @markp-fuso,您是否删除了 .xyz 文件的第一行(其中包含“6”的文件)? @markp-fuso,抱歉,我应该像您一样说,可以修改脚本以访问正确的行。对于我的特殊情况,删除该行还有另一个好处。谢谢你们俩的帮助。我的文件有数十万行长,使用 Awk 方法来处理它们比我所做的要快得多,也更可靠。 @markp-fuso:是的,我忽略了在文件中包含“6”行。我已经更新了答案,并留下了原始脚本以供参考。【参考方案2】:

假设:

匹配基于i(又名step)和time

step / time / Ax 数据文件:

$ cat match.dat
    #   Step       Time        Ax                   Ay                  Az                  Bx                  By                  Bz                  Cx                  Cy                  Cz                     Final
           0       0.000       14.8032123290        0.0000000000        0.0000000000        0.0000000000       14.8032123290        0.0000000000        0.0000000000        0.0000000000       14.8032123290          3243.9033438318
           1       0.500       14.8029498502        0.0000000000        0.0000000000        0.0000000000       14.8029498502        0.0000000000        0.0000000000        0.0000000000       14.8029498502          3243.7307919244
           2       1.000       14.8026923814        0.0000000000        0.0000000000        0.0000000000       14.8026923814        0.0000000000        0.0000000000        0.0000000000       14.8026923814          3243.5615395313
           3       1.500       14.8024398604        0.0000000000        0.0000000000        0.0000000000       14.8024398604        0.0000000000        0.0000000000        0.0000000000       14.8024398604          3243.3955453870
           4       2.000       14.8021922354        0.0000000000        0.0000000000        0.0000000000       14.8021922354        0.0000000000        0.0000000000        0.0000000000       14.8021922354          3243.2327751298
           100       50.000       14.8032123290        0.0000000000        0.0000000000        0.0000000000       14.8032123290        0.0000000000        0.0000000000        0.0000000000       14.8032123290          3243.9033438318
           200       100.000       14.8029498502        0.0000000000        0.0000000000        0.0000000000       14.8029498502        0.0000000000        0.0000000000        0.0000000000       14.8029498502          3243.7307919244
           300       150.000       14.8026923814        0.0000000000        0.0000000000        0.0000000000       14.8026923814        0.0000000000        0.0000000000        0.0000000000       14.8026923814          3243.5615395313
           301       150.500       14.8024398604        0.0000000000        0.0000000000        0.0000000000       14.8024398604        0.0000000000        0.0000000000        0.0000000000       14.8024398604          3243.3955453870
           400       200.000       14.8021922354        0.0000000000        0.0000000000        0.0000000000       14.8021922354        0.0000000000        0.0000000000        0.0000000000       14.8021922354          3243.2327751298

一个awk想法:

awk '
FNR==NR    if (FNR>1)                        # skip header line in 1st file
               Ax[$1 OFS $2]=$3               # use step + OFS + time as index for Ax[] array
            next
          

$1 == "i"  gsub(/,/,"")                      # remove commas from line so we can use normal FS delimiter to pull ...
            i=$3                              # field #3 (i) and ...
            time=$6                           # field #6 (time)
            if ( (i OFS time) in Ax)          # if i + OFS + time is an index in Ax[] array ...
                print i,time,Ax[i OFS time]   # print our 3 values to stdout
          
' match.dat [0-9]*.xyz

这会生成:

0 0.000 14.8032123290
100 50.000 14.8032123290
200 100.000 14.8029498502
300 150.000 14.8026923814
400 200.000 14.8021922354

如果 OP 需要用漂亮的列显示输出,一个想法是将结果通过管道传输到 column,例如:

$ awk '...' match.dat [0-9]*.xyz | column -t
0    0.000    14.8032123290
100  50.000   14.8032123290
200  100.000  14.8029498502
300  150.000  14.8026923814
400  200.000  14.8021922354

注意:此代码匹配字符串的完全匹配; 不是基于数值的匹配;所以150 != 150.00

【讨论】:

关于字符串格式和使用的重要提示。感谢您竭尽全力帮助解决此问题。【参考方案3】:

这可能对你有用(GNU sed):

sed -En '2~100s/^((\s*\S+)3).*/\1/p' file

打开扩展正则表达式并关闭隐式打印-En

从第 2 行(标题后面的行)开始,使用模数 100 选择所需的行,然后使用替换命令仅保留前三列。

如果必须使用初始文件进行匹配,那么:

sed -En 's/^i =\s*(\S+),.*/s#^\(\\s\*\1\(\\s\*\\S+\)\2\\)\.\*#\\1#p/p' file1of5 |
sed -Enf - fileWithStep

这会生成一组 sed 命令,这些命令提取步长值并将它们与具有步长值的文件进行匹配,并仅检索前 3 列。

注意这五个文件可以连接起来,也可以根据第一组 sed 命令单独命名,即将file1of5 替换为file1 file2 file3 file4 file5 如果它们包含唯一的步骤值。

【讨论】:

以上是关于是否可以从第一列包含与其他文件名称匹配的标识符的文件中提取某些行?的主要内容,如果未能解决你的问题,请参考以下文章

CSV的规范与使用

允许在资源管理器样式的列表视图中选择从第一列开始

PySpark查找另一列中是否存在一列中的模式

如何匹配两个csv文件中的浮点值?

Python:从第二列和第三列绘制,同时从第一列中选​​择参数值

awk / sed:如果任何字段与模式匹配,则替换所有字段