将CSV列中的值转换为单独的列

1 投票
2 回答
541 浏览
提问于 2025-04-17 06:22

我有一个CSV文件,格式如下:

ID | STUFF |  Custom | Custom Value
1  | string1 | name1 | val1
1  | string1 | name2 | val2
1  | string1 | name3 | val3
2  | string2 | name1 | val4
2  | string2 | name3 | val5
3  | string3 | name2 | val6

等等...

关于这个CSV文件,关键是当前的自定义列里有很多“字段”,我需要把这些字段分出来,放到自己的列里,并且把它们的值放在旁边的列中。这个自定义列里的值有点不确定,比如每个ID可能有不同的自定义“名称”。不过,我知道所有可能的“自定义”名称的完整列表。

我想要的输出结果是:(注意:我意识到我之前说的输出需求有点错误,现在已经更正了)

ID | STUFF    | name1       | name2      | name3

1  | SomeText | name1_Value | name2_Value| name3_Value
2  | SomeText | name1_Value | name2_Value| name3_Value

我对Python还比较陌生,现在在想怎么优雅地实现这个功能时遇到了困难,感觉需要很多次循环。我的想法是使用CSV模块和DictReader配合元组来解决这个问题,但目前我还在挣扎。我这个文件大约有1200行,只需要处理一次,但我想学习在Python中做事情的最佳方法。

2 个回答

0

csv模块绝对是个不错的起点。

我会为每个ID建立一个字典,把字段名和对应的值关联起来。例如,对于ID 1:

{'STUFF':'String 1', 'name1':'val1', 'name2':'val2', 'name3':'val3'}

你可以把这些存放在一个列表里(如果你的ID是连续的整数),或者放在另一个字典里。

保持一个你见过的所有字段名的集合。然后使用csv的DictWriter把结果输出成你想要的格式。遍历你的列表(用enumerate)或者字典(用d.iteritems()),把ID再加回每个字典里,然后发送给writerow。

0

你可以这样做(假设csv文件中的行是按id排序的):

import csv, itertools, operator
with open('data.csv', 'rb') as infile:
    results = []
    # uses the header row to get field names, each row will be a dict
    rows = csv.DictReader(infile)
    # keeps track of all the custom names we've seen
    all_custom_vals = set()
    for id_val, group in itertools.groupby(rows, operator.itemgetter('ID')):
        collapsed_row = {}
        for row in group:
            collapsed_row['ID'] = row['ID']
            collapsed_row['STUFF'] = row['STUFF']
            collapsed_row[row['Custom']] = row['Custom Value']
            all_custom_vals.add(row['Custom'])
        results.append(collapsed_row)

itertools.groupby 在这种情况下非常有用。

然后,results 将会是一个字典的列表,你可以用下面的方式把它写成csv格式:

import sys
writer = csv.writer(sys.stdout)
keys = sorted(all_custom_vals)
writer.writerow(['ID', 'STUFF'] + keys)
for row in results:
    items = [row['ID'], row['STUFF']]
    for key in keys:
        items.append(row.get(key, '<no value>'))
    writer.writerow(items)

<no value> 替换成当没有对应自定义名称的行时应该显示的值。

补充说明:其实,我给出的输出结果并不是你最初要求的(不过我觉得可能更有用)。如果你想要完全符合你要求的结果,你需要把第二部分改成:

import sys
writer = csv.writer(sys.stdout)
keys = sorted(all_custom_vals)
for row in results:
    items = [row['ID'], row['STUFF']]
    for key in keys:
        items.append(key)
        items.append(row.get(key, '<no value>'))
    writer.writerow(items)

撰写回答