【问题标题】:How to read in a .txt file when the column names are inline with the data with Pandas?当列名与 Pandas 的数据内联时,如何读取 .txt 文件?
【发布时间】:2021-03-11 19:53:19
【问题描述】:

我正在尝试使用 Pandas 将 .txt 文件读入数据框。我遇到的问题是列名与每一行的数据一致。这让我很难只获取数据,因为我不确定分隔符应该是什么。我的数据如下所示:(完整文件here

f= Al N= 1 rho[g/cc]=   0.269861 V[A^3]=     166.02561792 T[K]=    2020958 P[GPa]=       1877.100    24.300 E[Ha]=       -59.56300000  1.39000000

f= Al N= 1 rho[g/cc]=   0.269861 V[A^3]=     166.02561792 T[K]=    4041916 P[GPa]=       4249.300    18.400 E[Ha]=       160.64900000  1.07400000

f= Al N= 1 rho[g/cc]=   0.269861 V[A^3]=     166.02561792 T[K]=    8083831 P[GPa]=       9208.000    31.500 E[Ha]=       513.26500000  1.80900000

我尝试做的是:

Al = pd.read_csv('Al_EOS_09-18-20.txt', skiprows=18, delimiter='=', names=['f', 'N', 'rho[g/cc]', 'V[A^3]', 'T[K]', 'P[GPa]', 'E[Ha]'])

这会返回一个包含正确列的数据框,但每列下的值包含下一列的值和名称。所以在 Al['f'] 下我得到 'Al N' 而不仅仅是 'Al'。

任何帮助将不胜感激!

【问题讨论】:

  • 似乎很多人都在使用read_csv 来处理与 CSV 无关的事情。编写脚本将此文件转换为 Pandas 乐于接受的标准 CSV 格式大约需要 15 分钟。
  • @TimRoberts 人们使用他们熟悉的工具。说“这将需要... 15 分钟...将其转换为 [CSV]”是在加载我认为不安全的假设。 OP 清楚地认识到了一些问题,并在询问如何做得更好。

标签: python pandas dataframe file


【解决方案1】:

pd.read_fwf

固定宽度

import pandas as pd
import io
import requests
url = "http://militzer.berkeley.edu/FPEOS/files/Al_EOS_09-18-20.txt"
s = requests.get(url).content.decode('utf-8')

df = pd.read_fwf(
    io.StringIO(s), skiprows=18, header=None,
    usecols=[1, 3, 5, 7, 9, 11, 12, 14, 15],
    names='f N rho[g/cc] V[A^3] T[K] P[GPa]0 P[GPa]1 E[Ha]0 E[Ha]1'.split()
)

df

      f  N  rho[g/cc]      V[A^3]       T[K]    P[GPa]0  P[GPa]1     E[Ha]0  E[Ha]1
0    Al  1   0.269861  166.025618    2020958     1877.1     24.3    -59.563   1.390
1    Al  1   0.269861  166.025618    4041916     4249.3     18.4    160.649   1.074
2    Al  1   0.269861  166.025618    8083831     9208.0     31.5    513.265   1.809
3    Al  1   0.269861  166.025618   16167663    18629.3     36.3   1055.213   2.077
4    Al  1   0.269861  166.025618   32335325    37424.5     33.7   2129.898   1.924
..   .. ..        ...         ...        ...        ...      ...        ...     ...
235  Al  1   2.383356    1.383547   16167663  2126283.1   5177.3    938.925   2.532
236  Al  1   2.383356    1.383547   32335325  4411441.4   4979.6   2046.679   2.373
237  Al  1   2.383356    1.383547   64670651  8944752.2   4660.1   4212.630   2.231
238  Al  1   2.383356    1.383547  129341301  7991672.2   4802.6   8525.231   2.314
239  Al  1   2.383356    1.383547  215568835    17138.4   7894.0  14252.907   8.470

[240 rows x 9 columns]

【讨论】:

  • 这真的很有帮助!只是出于好奇,为什么这种方式需要将 p[GPa] 和 E[Ha] 分成 2 列?是和 pd.read_fwf 还是文件读入 s 的方式有关?
  • 您实际上可以使用read_fwf 执行更多操作,以更精确地指定列。默认情况下,它只是识别空白的位置和意义。
  • 在每一行中,这些列有两个值。 Pandas 对象最适用于标量和数值数据类型。将这些元素作为列表或元组包含在单列中会严重限制您可以对这些列执行的操作
  • 谢谢@PaulH。我也应该指出这一点。
  • 感谢两位的洞察力!
【解决方案2】:

由于您知道列的名称,因此理论上您也知道分隔符。你可以做一些正则表达式。

import pandas as pd
from io import StringIO

s = """f= Al N= 1 rho[g/cc]= 0.269861 V[A^3]= 166.02561792 T[K]= 2020958 P[GPa]= 1877.100 24.300 E[Ha]= -59.56300000 1.39000000
f= Al N= 1 rho[g/cc]= 0.269861 V[A^3]= 166.02561792 T[K]= 4041916 P[GPa]= 4249.300 18.400 E[Ha]= 160.64900000 1.07400000
f= Al N= 1 rho[g/cc]= 0.269861 V[A^3]= 166.02561792 T[K]= 8083831 P[GPa]= 9208.000 31.500 E[Ha]= 513.26500000 1.80900000"""


sep = 'f= |N= |rho\[g/cc]= |V\[A\^3]= |T\[K]= |P\[GPa]= |E\[Ha]= '

df = pd.read_csv(StringIO(s), sep=sep, 
                 names=['f', 'N', 'rho[g/cc]', 'V[A^3]', 'T[K]', 'P[GPa]', 'E[Ha]'],
                 engine='python').reset_index(drop=True)


     f  N  rho[g/cc]      V[A^3]     T[K]            P[GPa]  \
0  Al   1   0.269861  166.025618  2020958  1877.100 24.300    
1  Al   1   0.269861  166.025618  4041916  4249.300 18.400    
2  Al   1   0.269861  166.025618  8083831  9208.000 31.500    

                     E[Ha]  
0  -59.56300000 1.39000000  
1  160.64900000 1.07400000  
2  513.26500000 1.80900000  

【讨论】:

  • 非常感谢您的帮助!我以前没有使用过正则表达式,所以我在没有他们的情况下努力解决这个问题哈哈。
  • @Crusher101 在正则表达式中,| 表示“或”,\ 用于转义特殊字符,如 [^,在正则表达式中表示其他含义。
【解决方案3】:

一种选择是创建虚拟对象来存储不需要的值,然后在您提取数据后将其删除:

names = [1, 'f', 2, 'N', 3, 'rho[g/cc]', 4, 'V[A^3]', 5, 'T[K]', 6, 'P[GPa]0', 'P[GPa]1', 7, 'E[Ha]0', 'E[Ha]1']
df = pd.read_csv('test.txt',
                 sep='\s+',
                 index_col=False,
                 names=names)
df.drop(range(1, 8), axis=1, inplace=True)

df

    f  N  rho[g/cc]      V[A^3]     T[K]  P[GPa]0  P[GPa]1   E[Ha]0  E[Ha]1
0  Al  1   0.269861  166.025618  2020958   1877.1     24.3  -59.563   1.390
1  Al  1   0.269861  166.025618  4041916   4249.3     18.4  160.649   1.074
2  Al  1   0.269861  166.025618  8083831   9208.0     31.5  513.265   1.809

【讨论】:

  • 天哪,这么简单,真不敢相信我没想到!
  • 很高兴听到 :) 顺便说一句,您的实现假设通过设置分隔符,您会自动设置数据的列和值。实际上,delimiter 是 'sep' 的别名,它只是以与 str.split() 类似的方式分割你的行,并相应地设置给定名称的值。
【解决方案4】:

我会使用正则表达式而不是使用 pandas,然后构造一个数据框

import re

text = '''f= Al N= 1 rho[g/cc]=   0.269861 V[A^3]=     166.02561792 T[K]=    2020958 P[GPa]=       1877.100    24.300 E[Ha]=       -59.56300000  1.39000000
f= Al N= 1 rho[g/cc]=   0.269861 V[A^3]=     166.02561792 T[K]=    4041916 P[GPa]=       4249.300    18.400 E[Ha]=       160.64900000  1.07400000
f= Al N= 1 rho[g/cc]=   0.269861 V[A^3]=     166.02561792 T[K]=    8083831 P[GPa]=       9208.000    31.500 E[Ha]=       513.26500000  1.80900000
f= Al N= 1 rho[g/cc]=   0.269861 V[A^3]=     166.02561792 T[K]=   16167663 P[GPa]=      18629.300    36.300 E[Ha]=      1055.21300000  2.07700000
f= Al N= 1 rho[g/cc]=   0.269861 V[A^3]=     166.02561792 T[K]=   32335325 P[GPa]=      37424.500    33.700 E[Ha]=      2129.89800000  1.92400000
f= Al N= 1 rho[g/cc]=   0.269861 V[A^3]=     166.02561792 T[K]=   64670651 P[GPa]=      75127.600    29.100 E[Ha]=      4284.24400000  1.66400000
'''

m = re.findall('f=\s*(.+?)\s*N=\s*(.+?)\s*rho\[g\/cc\]=\s*(.+?)\s*V\[A\^3\]=\s*(.+?)\s*T\[K\]=\s*(.+?)\s*P\[GPa\]=\s*(.+?)\s*E\[Ha\]=\s*(.*)', text)


df6 = pd.DataFrame()

df6[['f','N','rho[g/cc]', 'V[A^3]','T[K]', 'P[GPa]','E[Ha]']] = pd.DataFrame(m)
df6

如果您不了解正则表达式,它们很容易学习,您可以在 https://regex101.com/ 等网站上对其进行测试。无论如何,我会解释我使用的那个。

\s* 表示出现零次或多次空白字符 .+?至少需要一个字符 () 用于提取正则表达式。

请记住,您可以搜索特定类型的字符。例如,如果您需要一位或多位数字,请使用 d+,其中 d 代表数字,+ 表示一位或多位。

希望我能提供帮助,即使答案与您使用的逻辑有点不同。

【讨论】:

  • 感谢您的解释!我对正则表达式不是很熟悉,所以引导我浏览你的代码对我来说非常有用。
【解决方案5】:

您可以编写一个自定义解析器并生成一个字典列表以传递给数据帧构造函数。

注意: 这会将具有 2 个浮点数的列作为字符串保留。您必须单独解析它们。

import pandas as pd
import io
import requests
url = "http://militzer.berkeley.edu/FPEOS/files/Al_EOS_09-18-20.txt"
s = requests.get(url).content.decode('utf-8')

def parse_line(line):
    line = [x.strip() for x in line.split('=')]
    line[0] = (None, line[0])
    line[-1] = (line[-1], None)
    line[1:-1] = [x.rsplit(maxsplit=1) for x in line[1:-1]]
    (_, *values), (*keys, _) = zip(*line)
    return dict(zip(keys, values))

df = pd.DataFrame(map(parse_line, s.splitlines()[18:]))

df

      f  N  rho[g/cc]        V[A^3]       T[K]                  P[GPa]                       E[Ha]
0    Al  1   0.269861  166.02561792    2020958      1877.100    24.300    -59.56300000  1.39000000
1    Al  1   0.269861  166.02561792    4041916      4249.300    18.400    160.64900000  1.07400000
2    Al  1   0.269861  166.02561792    8083831      9208.000    31.500    513.26500000  1.80900000
3    Al  1   0.269861  166.02561792   16167663     18629.300    36.300   1055.21300000  2.07700000
4    Al  1   0.269861  166.02561792   32335325     37424.500    33.700   2129.89800000  1.92400000
..   .. ..        ...           ...        ...                     ...                         ...
249  Al  1  32.383356    1.38354684   16167663   2126283.100  5177.300    938.92500000  2.53200000
250  Al  1  32.383356    1.38354684   32335325   4411441.400  4979.600   2046.67900000  2.37300000
251  Al  1  32.383356    1.38354684   64670651   8944752.200  4660.100   4212.63000000  2.23100000
252  Al  1  32.383356    1.38354684  129341301  17991672.200  4802.600   8525.23100000  2.31400000
253  Al  1  32.383356    1.38354684  215568835  30017138.400 17894.000  14252.90700000  8.47000000

[254 rows x 7 columns]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-04-05
    • 2019-09-09
    • 2021-05-30
    • 1970-01-01
    • 1970-01-01
    • 2015-10-17
    • 2023-03-26
    • 2019-02-16
    相关资源
    最近更新 更多