将具有不同键值对的文本文件转换为 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 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
我认为这是完成此任务最快的方法之一,因为它利用了 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 多处理。但是我上面的代码被剪掉了,避免了所有这些麻烦。
我有一个示例中的文本文件,我想将其转换为 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 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
我认为这是完成此任务最快的方法之一,因为它利用了 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 多处理。但是我上面的代码被剪掉了,避免了所有这些麻烦。