从大文件中提取数据的更快方法
Faster way to extract data from large file
我的文件包含大约 40000 帧 28 个原子的笛卡尔坐标。我需要从每一帧中提取原子 21 到 27 的坐标。
我尝试使用 bash 脚本和 for-loop。
for i in {0..39999}
do
cat | grep -A 27 "frame $i " | tail -n 6 | awk '{print , , }' >> new_coors.xyz
done
数据具有以下形式:
28
-1373.82296 frame 0 xyz file generated by terachem
Re 1.6345663991 0.9571586961 0.3920887712
N 0.7107677071 -1.0248027788 0.5007181135
N -0.3626961076 1.1948218124 -0.4621264246
C -1.1299268126 0.0792071086 -0.5595954110
C -0.5157993503 -1.1509115191 -0.0469223696
C 1.3354467762 -2.1017253883 1.0125736017
C 0.7611763218 -3.3742177216 0.9821756556
C -1.1378354025 -2.4089069492 -0.1199253156
C -0.4944655989 -3.5108477831 0.4043826684
C -0.8597552614 2.3604180994 -0.9043060625
C -2.1340008843 2.4846545826 -1.4451933224
C -2.4023114639 0.1449111237 -1.0888703147
C -2.9292779079 1.3528434658 -1.5302429615
H 2.3226814021 -1.9233467458 1.4602019023
H 1.3128699342 -4.2076373780 1.3768411246
H -2.1105470176 -2.5059031902 -0.5582958817
H -0.9564415355 -4.4988963635 0.3544299401
H -0.1913951275 3.2219343258 -0.8231465989
H -2.4436044324 3.4620639189 -1.7693069306
H -3.0306593902 -0.7362803011 -1.1626515622
H -3.9523215784 1.4136948699 -1.9142814745
C 3.3621999538 0.4972227756 1.1031860016
O 4.3763020637 0.2022266109 1.5735343064
C 2.2906331057 2.7428149541 0.0483795630
O 2.6669163864 3.8206298898 -0.1683800650
C 1.0351398442 1.4995168190 2.1137684156
O 0.6510904387 1.8559680025 3.1601927094
Cl 2.2433490373 0.2064711824 -1.9226174036
它有效,但需要大量时间,
将来我将使用更大的文件。有更快的方法吗?
如果文件中的帧编号已经排序,例如他们的顺序是数字 0 - 39999,那么也许像这样的东西可以完成这项工作(没有测试,因为我们没有样本输入文件,正如 Jepessen 所建议的):
cat | grep -A 27 -E "frame [0-9]+ " | \
awk '{if ( == "frame") n = 0; if (n++ > 20) print , , }' > new_coors.xyz
(上面的代码明确地冗长,以便于理解并更接近您现有的脚本。如果您需要更紧凑的解决方案,请查看 kvantour 答案)
您的程序运行缓慢的原因是您在 for 循环中不断地重新读取输入文件。你可以通过一次读取你的文件来完成所有的事情,然后使用 awk 代替:
awk '/frame/{c=0;next}{c++}(c>20 && c<27){ print ,, }' input > output
此答案采用以下数据形式:
frame ???
??? x y z ???
??? x y z ???
...
frame ???
??? x y z ???
??? x y z ???
...
该解决方案检查它是否在一行中找到单词 frame
。如果是,它将原子计数器 c
设置为零并跳到下一行。从那时起,如果读取新行,它将始终读取增加计数器。如果计数器在 20 和 27 之间(不包括),它将打印坐标。
你现在可以很容易地对此进行扩展:假设你想要相同的原子,但只需要从第 1000 帧到第 1500 帧。你可以通过引入一个帧计数器来做到这一点 fc
awk '/frame/{fc++;c=0;next}{c++}(fc>=1000 && fc <=1500) && (c>20 && c<27){ print ,, }' input > output
您也许可以使用 2 遍 grep
,而不是数千遍?
假设您想要在每一帧之后显示第 21-27 行,并且您不想记录帧编号本身,那么以下短语应该会得到您想要的行,然后您可以 'tidy' awk:
grep -A27 ' frame ' | grep -B6 '-----'
如果您还想要帧号(我看不到任何证据),或者您真的想限制帧号的范围,您可以使用 tee 和 >( grep 'frame') 来生成一个然后您需要重新合并的第二个文件。如果您将 -n 添加到 grep,那么您可以轻松地根据行号对文件进行合并排序。
另一种无需多次传递即可限制帧数的方法是使用更复杂的 grep 表达式来描述数字范围(-E,因为反引号的寿命太短):
-E ' frame (([0-9]{1,4}|[0-3][0-9]{1,4}) '
我的文件包含大约 40000 帧 28 个原子的笛卡尔坐标。我需要从每一帧中提取原子 21 到 27 的坐标。
我尝试使用 bash 脚本和 for-loop。
for i in {0..39999}
do
cat | grep -A 27 "frame $i " | tail -n 6 | awk '{print , , }' >> new_coors.xyz
done
数据具有以下形式:
28
-1373.82296 frame 0 xyz file generated by terachem
Re 1.6345663991 0.9571586961 0.3920887712
N 0.7107677071 -1.0248027788 0.5007181135
N -0.3626961076 1.1948218124 -0.4621264246
C -1.1299268126 0.0792071086 -0.5595954110
C -0.5157993503 -1.1509115191 -0.0469223696
C 1.3354467762 -2.1017253883 1.0125736017
C 0.7611763218 -3.3742177216 0.9821756556
C -1.1378354025 -2.4089069492 -0.1199253156
C -0.4944655989 -3.5108477831 0.4043826684
C -0.8597552614 2.3604180994 -0.9043060625
C -2.1340008843 2.4846545826 -1.4451933224
C -2.4023114639 0.1449111237 -1.0888703147
C -2.9292779079 1.3528434658 -1.5302429615
H 2.3226814021 -1.9233467458 1.4602019023
H 1.3128699342 -4.2076373780 1.3768411246
H -2.1105470176 -2.5059031902 -0.5582958817
H -0.9564415355 -4.4988963635 0.3544299401
H -0.1913951275 3.2219343258 -0.8231465989
H -2.4436044324 3.4620639189 -1.7693069306
H -3.0306593902 -0.7362803011 -1.1626515622
H -3.9523215784 1.4136948699 -1.9142814745
C 3.3621999538 0.4972227756 1.1031860016
O 4.3763020637 0.2022266109 1.5735343064
C 2.2906331057 2.7428149541 0.0483795630
O 2.6669163864 3.8206298898 -0.1683800650
C 1.0351398442 1.4995168190 2.1137684156
O 0.6510904387 1.8559680025 3.1601927094
Cl 2.2433490373 0.2064711824 -1.9226174036
它有效,但需要大量时间, 将来我将使用更大的文件。有更快的方法吗?
如果文件中的帧编号已经排序,例如他们的顺序是数字 0 - 39999,那么也许像这样的东西可以完成这项工作(没有测试,因为我们没有样本输入文件,正如 Jepessen 所建议的):
cat | grep -A 27 -E "frame [0-9]+ " | \
awk '{if ( == "frame") n = 0; if (n++ > 20) print , , }' > new_coors.xyz
(上面的代码明确地冗长,以便于理解并更接近您现有的脚本。如果您需要更紧凑的解决方案,请查看 kvantour 答案)
您的程序运行缓慢的原因是您在 for 循环中不断地重新读取输入文件。你可以通过一次读取你的文件来完成所有的事情,然后使用 awk 代替:
awk '/frame/{c=0;next}{c++}(c>20 && c<27){ print ,, }' input > output
此答案采用以下数据形式:
frame ???
??? x y z ???
??? x y z ???
...
frame ???
??? x y z ???
??? x y z ???
...
该解决方案检查它是否在一行中找到单词 frame
。如果是,它将原子计数器 c
设置为零并跳到下一行。从那时起,如果读取新行,它将始终读取增加计数器。如果计数器在 20 和 27 之间(不包括),它将打印坐标。
你现在可以很容易地对此进行扩展:假设你想要相同的原子,但只需要从第 1000 帧到第 1500 帧。你可以通过引入一个帧计数器来做到这一点 fc
awk '/frame/{fc++;c=0;next}{c++}(fc>=1000 && fc <=1500) && (c>20 && c<27){ print ,, }' input > output
您也许可以使用 2 遍 grep
,而不是数千遍?
假设您想要在每一帧之后显示第 21-27 行,并且您不想记录帧编号本身,那么以下短语应该会得到您想要的行,然后您可以 'tidy' awk:
grep -A27 ' frame ' | grep -B6 '-----'
如果您还想要帧号(我看不到任何证据),或者您真的想限制帧号的范围,您可以使用 tee 和 >( grep 'frame') 来生成一个然后您需要重新合并的第二个文件。如果您将 -n 添加到 grep,那么您可以轻松地根据行号对文件进行合并排序。
另一种无需多次传递即可限制帧数的方法是使用更复杂的 grep 表达式来描述数字范围(-E,因为反引号的寿命太短):
-E ' frame (([0-9]{1,4}|[0-3][0-9]{1,4}) '