【问题标题】:Write Pandas DataFrame to file using FORTRAN format string使用 FORTRAN 格式字符串将 Pandas DataFrame 写入文件
【发布时间】:2015-09-03 10:35:06
【问题描述】:

我想使用 FORTRAN 格式字符串将 pandas 数据帧写入文件。除了讨论这个功能如何更好之外,我无法在网上找到任何东西。有谁知道这是否可能?

我想我不需要使用 fortran 格式字符串...我只需要获取 fortran 可以轻松读取的特定格式的输出文件。

更新:例如,我有一个具有指定 fortran 格式的大型数据文件。我将文件加载到我的 python 函数中,操作数据,然后想将操作后的数据导出到与最初格式相同的文件中。文件格式的示例类似于:

FORMAT (1X,F12.6,2F9.6,F11.7,T61,2F9.6,F10.7,T142,I6,1X,A2,T236,A1)

我需要以特定格式导出数据的原因是因为输出文件将被直接读入成熟的 fortran 代码(意味着不能更改 fortran 代码)。

【问题讨论】:

  • 我不知道 pandas 格式的字符串是什么样子的……
  • 你为什么不直接使用 csv?
  • 我认为 WillaB 要求提供表格输出。每个变量应该从每一行的同一列开始,而对于 CSV,它可以根据值而变化。但也许这可以用 CSV 和格式来完成?我不知道。 @WillaB -- 样本数据和所需的输出会使这一点更清楚。
  • fortran 不需要这样的列结构。
  • 没有人说 fortran 需要列结构,但在 fortran 中这是一种常见的方法。我认为这是解决问题的合理方法,但不是唯一方法,您可以采取任何您喜欢的方法。

标签: python pandas file-format


【解决方案1】:

我知道这并不理想,但我采取的方法是逐行打印出每条记录:

df = pd.DataFrame({'alt':[1435.2, 1234.7], 'lat':[0.145, 0.324], 'lon':[12.45, 12.23]})

with open('flight.trk', 'w') as f:
    f.write("! Alt Lat Lon\n")
    for ix, alt, lat, lon in df.itertuples():
        f.write("{:10.2f} {:9.4f} {:9.4f}\n".format(alt, lat, lon))

请注意,我已按照此处的建议 (What is the most efficient way to loop through dataframes with pandas?) 使用 itertuples 来遍历行,但这依赖于知道列的顺序(在这种情况下是按字母顺序排列的)。

我已经将它用于超过 10,000 行的表格,尽管我没有进行任何严格的时序实验,但根据我的经验,它实际上相当快。

【讨论】:

    【解决方案2】:

    更新:

    我现在分两步做这种事情:

    • 第 1 步 -- 从 pandas 数据帧转换为 numpy 数组或 rec-array。这通过valuesto_numpy 方法是微不足道的。如果您有字符串,则有点棘手,但请参阅here 了解一种技术。如果您有简单的数字数据(并且没有字符串),请坚持使用常规的 numpy 数组,不要使用 rec-array 或结构化数组。

    • 第 2 步 -- 使用 numpy 的 tofile 写出 Fortran 可读的二进制文件

    原答案:

    我想更大的问题是如何从 pandas 输出到 fortran,我不确定最好的方法,但我会尝试展示一些相当简单的解决方案,主要是 to_csv()

    这样做总是会为您提供更快的 IO,实际上我发现在这种情况下二进制比文本更容易,尽管您确实失去了将数据视为文本的能力。

    df = pd.DataFrame({ 'x':[1.03,2.9,3.7],'y':[1,22,5] })
    
          x   y
    0  1.03   1
    1  2.90  22  
    2  3.70   5
    

    标准 pandas 输出实际上正是您在这里所要求的,但除了复制和粘贴外,我不确定如何将其放入文件中。也许 ipython 有办法(虽然不是我能找到的)。

    这是一些默认的 csv 输出,显然不是柱状的:

    df.to_csv('foo.csv',index=False)
    
    %more foo.csv
    x,y
    1.03,1
    2.9,22
    3.7,5
    

    但您可以使用 list directed input 将其导入 fortran。

    如果您可以对所有数字使用相同的格式,您可以这样做:

    df.astype(float).to_csv('foo.raw',index=False,float_format='%10.5f')
    
    %more foo.raw
    x,y
       1.03000,   1.00000
       2.90000,  22.00000
       3.70000,   5.00000
    

    这里有几点注意事项:这还不错,但限制了您对所有数字使用相同的格式,例如,这对于单个数字整数来说是非常浪费的。另外,我用一些 NaN 尝试了这个,但效果不是很好。而且那里也不需要逗号,但是当我尝试将分隔符更改为“”时,它引用了所有内容,所以我把它省略了。

    最后,最灵活的方法可能是转换为字符串并格式化。这使您可以灵活地单独设置每列的格式。这是一个使用右对齐格式的简单示例('x' 的宽度为 8,'y' 的宽度为 4):

    df.x = df.x.map('{:>8}'.format)
    df.y = df.y.map('{:>4}'.format)
    df.to_csv('foo.str',index=False)
    
    %more foo.str
    x,y
        1.03,   1
         2.9,  22
         3.7,   5
    

    我仍然不知道如何摆脱这些逗号,但这种方式确实可以成功处理 NaN。

    【讨论】:

    • 感谢您的这些尝试。不幸的是,每一列都需要单独处理(我不能对所有列使用相同的格式)。我还需要能够自动写出半大型数据文件,因为它们会直接从我的函数转换为完善的 fortran 代码。我将尝试你的最后一个建议,看看我是否可以去掉逗号。
    • @WillaB -- 逗号很烦人,但应该不是 fortran 格式的问题,对吧?除了浪费一些空间。或者,我意识到这并不理想,但它只是 shell 脚本中的一行代码或文本编辑器中的一个命令来删除逗号,这是一种快速而肮脏的方式。如果你真的希望它们通过 pandas 出来,你可能只想在这里提出一个关注这一点的后续问题。一定有办法,但我尝试了几个选项,但都没有奏效......
    • 如果逗号在两个变量输出之间创建一个附加列,它们可能会出现问题。例如,如果前 3 个变量是日、月、年,则如果它们的变量没有以 Fortran 代码格式的空格分隔,则逗号可能会出现问题:2111201521,11,2015
    • @WillaB 但是你可以告诉fortran它是两种情况中的哪一种。 2i2,i42(i2,x),i4。前一种语法当然要简单一些,但是使用基本的 fortran 输入当然可以跳过讨厌的列。
    • 但是您假设可以修改 fortran 代码格式语句。我也许可以根据我的目的修改代码,但这不是一个理想的解决方案。似乎熊猫 应该 有一种直接的方法来强制使用特定格式。令我惊讶的是,事实并非如此。再次感谢您的帮助。
    【解决方案3】:

    这是一个使用fortranformat 包(pip install fotranformathttps://pypi.org/project/fortranformat/)和df.apply() 的整洁解决方案,让您可以使用标准的fortran 格式字符串:

    import fortranformat as ff
    import pandas as pd 
    
    df = pd.DataFrame({
            'sampleId': ['A','B','C','D'],        
            'var1' : [0.002,0.004,0.006,0.002],
            'var2' : [1.2,1.4,1.6,1.2],
            'Nobs': [32,12,9,30]
        })
    
    format_string = '(a5, f8.3, f8.1, i5)'
    header_line = ff.FortranRecordWriter(format_string)
    Formatted_df = df.apply(lambda x : header_line.write(x.values),axis=1)
    

    Formatted_df 对象将是一个 Series,数据框的每一行都有一个字符串元素:

    >>> print(Formatted_df)
    0        A   0.002     1.2   32
    1        B   0.004     1.4   12
    2        C   0.006     1.6    9
    3        D   0.002     1.2   30
    dtype: object
    >>> print(Formatted_df.loc[0])
        A   0.002     1.2   32
    >>> print(type(Formatted_df.loc[0]))
    <class 'str'>
    

    要将其写入文件,您只需使用to_csv

    Formatted_df.to_csv('formatted_df.csv',index=False,header=False)
    

    请注意,这不会包含任何列名,因此您可能希望初始化输出文件然后附加到它:

    output_fi='formatted_df.csv'
    col_names=df.columns.tolist()
    with open(output_fi,'w') as outfi: 
        outfi.write('# '+' '.join(col_names)+"\n")
        outfi.write('# '+format_string+"\n")
        
    Formatted_df.to_csv(output_fi,mode='a',index=False,header=False)
    

    另请注意,这假设您已经知道数据框列的顺序。

    请注意,如果您处理非常大的数据帧,则可能会遇到内存问题,因为Formatted_df 将是df 的完整副本。如果是这种情况,您将不得不将其分块!

    【讨论】:

    • 这非常好用!感谢您提请我注意该包裹!
    【解决方案4】:

    稍后再介绍,这是一个适用于我的情况的解决方案(myDF 有第一列字符串,然后是 2 列浮点数)。假设你需要用 Fortran 阅读一些东西

    FORMAT (A19,F11.6,F11.6)
    

    然后(使用import numpy as np):

    np.savetxt(myfile, myDF.to_numpy(), fmt="%19s %10.6f %10.6f")
    

    或者(避免额外的空间,但不太清楚):

    np.savetxt(myfile, myDF.to_numpy(), fmt="%19s%11.6f%11.6f") 
    

    (以防万一它帮助别人;-))

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-08-10
      • 1970-01-01
      • 2013-05-31
      • 1970-01-01
      • 1970-01-01
      • 2015-12-14
      相关资源
      最近更新 更多