将具有不同键值对的文本文件转换为 csv 文件

Convert a text file with different key value pairs to a csv file

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

示例文件:

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 字符串)?

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 constructorfrom_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

我认为这是完成此任务最快的方法之一,因为它利用了 pandas 库的 built-in 多处理(用 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()

N.B:

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