Я немного расстроен из-за того, что пишу подобные ответы - кажется, что я просто создаю законченное решение с очень небольшим дидактическим значением, но я пытался сделать его как можно более полезным ...
Если я понимаю, что вы пытаетесь сделать правильно, вы хотите превратить ваш data
в CSV, где для каждого пользователя есть по одной строке. Есть диапазон дат, и вы хотите один столбец для каждой даты - этот столбец показывает статус пользователя на эту дату. Затем есть столбцы, которые выдают итоги для каждого статуса за каждую дату и так далее. Вывод, который вы цитировали, больше всего похож на CSV с вкладками в качестве разделителей, хотя, как указывает eumiro, это не совсем так. Однако предположим, что вы хотите записать данные, разделенные табуляцией. Из вашего вопроса не ясно, что должно произойти, если вы обнаружите в data
, что у пользователя два разных статуса на один день, поэтому давайте проверим это и сгенерируем исключение.
Обратите внимание, что все в последнем абзаце действительно должно быть в вашем вопросе, вместе с кодом из вашей лучшей попытки на данный момент.
Итак, использование DictWriter
из модуля csv является разумной идеей, но для использования этого класса вам необходим словарь для каждой строки, который отображает заголовки столбцов в значения. Таким образом, вы можете перебрать все в data
, чтобы создать словарь словарей, отображающий пользователя в словарь, который представляет строку для этого пользователя. Вы можете сделать это примерно так:
from collections import defaultdict
import csv
from datetime import date
user_to_row = defaultdict(dict)
for d in data:
user = d['user']
status = d['status']
row_dict = user_to_row[user]
row_dict['user'] = user
date_string = str(d['date'])
if date_string in d and row_dict[date_string] != status:
raise Exception, "Contradiction: '%s' on '%s'" % (user,date_string)
row_dict[date_string] = status
# If a value isn't set in one of the total columns yet, set it to 0:
row_dict.setdefault('p-total',0)
row_dict.setdefault('E-total',0)
row_dict.setdefault('total',0)
# Make sure you increment the right column:
count_column = 'p-total' if (status == 'P') else 'E-total'
row_dict[count_column] += 1
# And increment the overall total column in any case:
row_dict['total'] += 1
Вам следует убедиться, что вы понимаете, что там происходит - попробуйте напечатать user_to_row
, чтобы убедиться, что вы понимаете, что производится.
Теперь вам просто нужно перебрать значения в словаре user_to_row
и вывести их с помощью DictWriter. Здесь следует быть осторожным: вы не уверены, что для каждой даты будет запись, поэтому в этом случае я просто вставил Unknown
, когда значение отсутствует:
with open("hello.csv","w") as f:
# Create the headings:
headings = ['S.no']
headings += [str(date(2011,2,i)) for i in xrange(6,11)]
headings += ['user', 'date_format','p-total','E-total','total']
writer = csv.DictWriter(f, headings, delimiter="\t")
# The writeheader method only appeared in Python 2.7, so write the
# headings from a dictionary that maps each heading to itself:
writer.writerow(dict(zip(headings,headings)))
# Assume that S.no is just a row number...
sno = 1
for d in user_to_row.values():
d['S.no'] = sno
# Fill in any unknown values with 'Unknown':
for h in headings:
d.setdefault(h,'Unknown')
writer.writerow(d)
sno += 1
Документация для модуля CSV должна предоставить вам всю дополнительную информацию, необходимую для понимания этой части.
Выходные данные выглядят следующим образом:
S.no 2011-02-06 2011-02-07 2011-02-08 2011-02-09 2011-02-10 user date_format p-total E-total total
1 Unknown Unknown P P P xxx Unknown 3 0 3
2 Unknown Unknown P E P yyy Unknown 2 1 3
3 Unknown Unknown P E P zzz Unknown 2 1 3
... который выглядит странно из-за вкладок, но будет корректно загружаться в электронную таблицу.