Преобразовать словарь списков в CSV с двумя столбцами
У меня есть словарь списков следующим образом:
{'banana': [1,2],
'monkey': [5],
'cow': [1,5,0],
...}
Я хочу написать CSV, который содержит одно число и слово следующим образом:
1 | banana
2 | banana
5 | monkey
1 | cow
5 | cow
0 | cow
...
с | в качестве разделителя.
Я попытался преобразовать его в список кортежей и записать его следующим образом:
for k, v in dic.items():
for ID in v:
rv.append((ID, k))
with open(index_filename,'wb') as out:
csv_out=csv.writer(out, delimiter='|')
csv_out.writerow(['identifier','descriptor'])
for row in rv:
csv_out.writerow(row)
но запустил эту ошибку:
a bytes-like object is required, not 'str'
Есть ли более эффективный способ сделать это, чем преобразование в кортеж, и если нет, что не так с моим кодом?
Благодарю.
2 ответа
Вы открываете файл в двоичном / байтовом режиме, который указывается с помощью "b" в "wb". Это то, что многие люди делали в дни python2, когда "str" и "bytes" были одним и тем же, поэтому многие старые книги до сих пор учат этому.
Если вы открываете файл в режиме байтов, вы должны записывать в него байты, а не строки. str
можно преобразовать в байты с помощью str.encode()
метод:
f.write(some_str_variable.encode()
Однако, скорее всего, вам не нужно открывать файл в байтовом режиме.
with open(index_filename, 'w') as out:
...
Если вы хотите сделать свой код более эффективным, важно, чтобы вы указали, что хотите сделать его более эффективным. Помимо ужасных решений, между разумными решениями часто существует компромисс между пространством (память) и временем (циклы, вызовы функций).
Помимо эффективности, вы также должны принимать во внимание удобочитаемость и ремонтопригодность. Прежде чем делать какие-либо оптимизации.
Кортежи, подобные dicts в Python, очень эффективны, потому что они используются повсеместно. Большинство вызовов функций в Python включают создание кортежа (для позиционных аргументов) под капотом.
Что касается вашего конкретного примера, вы можете использовать выражение генератора, чтобы избежать временного списка:
entries = ((k, v) for k, l in dic.items() for v in l)
У вас все еще есть промежуточные кортежи, но они вычисляются на лету, пока вы перебираете элементы словаря. Это решение будет более эффективным в использовании памяти, чем явный список, особенно если у вас много записей.
Вы также можете просто поместить вложенный цикл непосредственно в тело with:
with open(index_filename,'wb') as out:
csv_out=csv.writer(out, delimiter='|')
csv_out.writerow(['identifier','descriptor'])
for k, v in dic.items():
for ID in v:
csv_out.writerow((k, ID))
Чтобы избежать повторных вызовов функций writerow
Вы также можете прибегнуть к writerows
, который может быть быстрее.
with open(index_filename,'wb') as out:
csv_out=csv.writer(out, delimiter='|')
csv_out.writerow(['identifier','descriptor'])
csv_out.writerows((k, v) for k, l in dic.items() for v in l)
Если вас действительно интересует, какой метод является самым быстрым, вы можете использовать модуль timeit Python для проведения измерений.