【问题标题】:Convert a text file with different key value pairs to a csv file将具有不同键值对的文本文件转换为 csv 文件
【发布时间】:2022-02-17 19:47:49
【问题描述】:

我有一个如下示例中所示的文本文件,我想将其转换为 csv 文件(当前使用 Pandas)。
挑战在于我事先不知道键(列标题是什么)以及它们的顺序。
最后的列顺序并不重要。

示例文件:

name: john| dob: 10-06-1960| address: 4853 Radio Park Drive
name: jane| dob: 07-10-1973| address: 1537 Timbercrest Road| mobile: 706-289-6746
name: liam| dob: 12-08-1986| address: 4853 498 Fairmont Avenue| telephone number: 706-687-5021
name: chris| dob: 09-12-1965| address: 485 Green Avenue| state: California| Telephone Number: 510-855-5213

期望的输出:

Name | dob        | address                  | mobile       | telephone number | state      |
-----+------------+--------------------------+--------------+------------------+------------+
john | 10-06-1960 | 4853 Radio Park Drive    |              |                  |            |
jane | 07-10-1973 | 1537 Timbercrest Road    | 706-289-6746 |                  |            |
liam | 12-08-1986 | 4853 498 Fairmont Avenue |              | 706-687-5021     |            |
chris| 09-12-1965 | 485 Green Avenue         |              | 510-855-5213     | California |

我的代码:

import pandas as pd

df = pd.DataFrame()

file = open('D:\sample.log', 'r')
        
lines = file.readlines()

for line in lines:

    pairs = line.split('|')

    my_dict = {}

    for pair in pairs:
       key = pair.split(': ')[0].strip()
       value = pair.split(': ')[1].strip()
            
       my_dict[key] = value

       df.append(my_dict, ignore_index=True)

这种追加方式很慢。我怎样才能让它更快。

或者有更好的解决方案(例如通过 json 字符串)?

【问题讨论】:

    标签: python pandas dataframe export-to-csv key-value


    【解决方案1】:

    TL;DR:

    pd.DataFrame.from_records(
        dict(field.split(': ') for field in line.split('|'))
        for line in lines
    )
    

    加长版

    假设您已经将数据拆分为行,那么您需要将它们处理成记录,例如:

    {' address': '4853 Radio Park Drive', ' dob': '10-06-1960', 'name': 'john'}
    

    每一行需要拆分成字段:

    >>> line = 'name: john| dob: 10-06-1960| address: 4853 Radio Park Drive'
    >>> line.split('|')
    ['name: john', ' dob: 10-06-1960', ' address: 4853 Radio Park Drive']
    

    然后每个字段需要拆分为列名和值本身:

    >>> field = 'name: John'
    >>> field.split(': ')
    ['name', 'john']
    

    对行中的每个字段执行此操作后,您最终会得到以下列表:

    >>> [field.split(': ') for field in line.split('|')]
    [['name', 'john'],
     [' dob', '10-06-1960'],
     [' address', '4853 Radio Park Drive']]
    

    使用此列表初始化的字典会从答案的开头为您获取记录。

    因为你有很多行,你需要产生很多记录,但最好是懒惰地产生这些,换句话说,使用generator

    >>> (dict(field.split(': ') for field in line.split('|')) for line in s.split('\n'))
    <generator object <genexpr> at 0x7f0d06bf8dd0>
    

    生成器不会为您生成完整的记录列表,而是在您迭代它时一次为您提供一个。这样您就可以开始形成数据框,而无需等待所有记录都被处理。

    Python 中有一种特殊的语法称为generator comprehension,让您可以定义生成器作为参数传递给函数和构造函数。

    综上所述,我们使用appropriate constructor (from_records) 和上面定义的生成器构造了一个数据框:

    pd.DataFrame.from_records(
        dict(field.split(': ') for field in line.split('|'))
        for line in lines
    )
    

    这会产生以下输出:

        name         dob                   address        mobile  telephone number       state  Telephone Number
    0   john  10-06-1960     4853 Radio Park Drive           NaN               NaN         NaN               NaN
    1   jane  07-10-1973     1537 Timbercrest Road  706-289-6746               NaN         NaN               NaN
    2   liam  12-08-1986  4853 498 Fairmont Avenue           NaN      706-687-5021         NaN               NaN
    3  chris  09-12-1965          485 Green Avenue           NaN               NaN  California      510-855-5213
    

    作为奖励,您也可以通过懒惰地读取文件来进一步加快速度。为阅读线定义一个自定义生成器:

    def lines(path):
      with open(path) as file:
        while line := file.readline():
          yield line.rstrip()
    

    请注意,这仅适用于 Python 3.8+。否则,您需要这样做,而不是使用 walrus operator

    def lines(path):
      with open(path) as file:
        while True:
          line = file.readline()
          if line:
            yield line.rstrip()
          else:
            return
    

    【讨论】:

    • 在您的示例中,datastring。我如何使用这个读取文件? (例如lines = file.readlines()
    • 要从字符串转到行使用data.split('\n'),我将答案调整为直接从行开始
    【解决方案2】:

    我认为这是完成这项任务的最快方法之一,因为它利用了 pandas 库的内置多处理(用 c/c++ 编写),比遍历行要快得多。

    首先将整个文本读入一个变量。然后,

    import pandas as pd
    
    data = '''name: john| dob: 10-06-1960| address: 4853 Radio Park Drive
    name: jane| dob: 07-10-1973| address: 1537 Timbercrest Road| mobile: 706-289-6746
    name: liam| dob: 12-08-1986| address: 4853 498 Fairmont Avenue| telephone number: 706-687-5021
    name: chris| dob: 09-12-1965| address: 485 Green Avenue| state: California| Telephone Number: 510-855-5213'''
    
    
    def get_dict(line_elems):
        line_dict = {}
        for elem in line_elems:
            k, v = elem.split(':')
            line_dict[k]=v
        return line_dict
    
    df = pd.DataFrame({'lines':data.split('\n')})
    df['line_list'] = df['lines'].apply(lambda x:x.split('|'))
    df['line_dict'] = df['line_list'].apply(get_dict)
    dict_list = df['line_dict'].tolist()
    final_df = pd.DataFrame.from_dict(dict_list)
    final_df
    

    如果你愿意,我可以解释代码,但请告诉我它与其他代码相比的性能。

    要从文件中读取整个文本,您可以使用

    with open('my_text_file',mode='r') as file:
        data= file.read()
    

    注意:

    • 如果您想要快速的性能,我认为您应该避免迭代行,无论您使用哪种方法。如果你在 python 方面足够先进,你可以尝试使用 Python Multiprocessing。但是我上面的代码被剪掉了,避免了所有这些麻烦。

    【讨论】:

      猜你喜欢
      • 2020-02-24
      • 1970-01-01
      • 1970-01-01
      • 2015-03-22
      • 1970-01-01
      • 2020-05-13
      • 2021-04-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多