我有一个从csv DictReader读入的词典列表,它代表了一个csv文件的行:
rows = [{"id":"123","date":"1/1/18","foo":"bar"},
{"id":"123","date":"2/2/18", "foo":"baz"}]
我想创建一个新的字典,其中只存储唯一的ID。但我想只保留最近日期的行条目。根据上面的例子,它将保持行的日期为2/2/18。
我正在考虑做这样的事情,但是在将else语句中的伪代码转换为实际的python时遇到了麻烦。
我可以弄清楚检查两个日期更近期的部分,但最麻烦的是弄清楚我如何检查包含相同id的字典的新列表,然后从该行检索日期。
注意:遗憾的是,由于我们平台上的资源限制,我无法在此项目中使用pandas。
new_data = []
for row in rows:
if row['id'] not in new_data:
new_data.append(row)
else:
check the element in new_data with the same id as row['id']
if that element's date value is less recent:
replace it with the current row
else :
continue to next row in rows
答案 0 :(得分:1)
import datetime
rows = [{"id":"123","date":"1/1/18","foo":"bar"},
{"id":"123","date":"2/2/18", "foo":"baz"}]
def parse_date(d):
return datetime.datetime.strptime(d, "%d/%m/%y").date()
tmp_dict = {}
for row in rows:
if row['id'] not in tmp_dict.keys():
tmp_dict['id'] = row
else:
if parse_date(row['date']) > parse_date(tmp_dict[row['id']]):
tmp_dict['id'] = row
print tmp_dict.values()
输出
[{'date': '2/2/18', 'foo': 'baz', 'id': '123'}]
注意:您可以将两个if
合并到if row['id'] not in tmp_dict.keys() || parse_date(row['date']) > parse_date(tmp_dict[row['id']])
,以获得更清晰,更短的代码
答案 1 :(得分:1)
您需要一个函数将日期(作为字符串)转换为日期(作为日期)。
import datetime
def to_date(date_str):
d1, m1, y1 = [int(s) for s in date_str.split('/')]
return datetime.date(y1, m1, d1)
我假设你的日期格式是d / m / yy。考虑使用datetime.strptime
来解析您的日期,如Alex Hall的回答所示。
然后,我们的想法是遍历你的行并将它们存储在一个新结构中(这里是一个dict,其键是ID)。如果密钥已存在,请将其日期与当前行进行比较,然后选择正确的行。遵循您的伪代码,这会导致:
rows = [{"id":"123","date":"1/1/18","foo":"bar"},
{"id":"123","date":"2/2/18", "foo":"baz"}]
new_data = dict()
for row in rows:
existing = new_data.get(row['id'], None)
if existing is None or to_date(existing['date']) < to_date(row['date']):
new_data[row['id']] = row
如果您希望new_data
变量成为列表,请使用new_data = list(new_data.values())
。
答案 2 :(得分:0)
首先,使用正确的日期对象,而不是字符串。以下是解析它们的方法:
field
(检查格式是否正确)
然后是实际任务:
from datetime import datetime, date
rows = [{"id": "123", "date": "1/1/18", "foo": "bar"},
{"id": "123", "date": "2/2/18", "foo": "baz"}]
for row in rows:
row['date'] = datetime.strptime(row['date'], '%d/%m/%y').date()
可替换地:
以下是一些在这里运行良好的通用实用程序函数,我在很多地方都使用它:
new_data = {}
for row in rows:
new_data[row['id']] = max(new_data.get(row['id'], date.min),
row['date'])
print(new_data.values())
然后解决方案可以写成:
from collections import defaultdict
def group_by_key_func(iterable, key_func):
"""
Create a dictionary from an iterable such that the keys are the result of evaluating a key function on elements
of the iterable and the values are lists of elements all of which correspond to the key.
"""
result = defaultdict(list)
for item in iterable:
result[key_func(item)].append(item)
return result
def group_by_key(iterable, key):
return group_by_key_func(iterable, lambda x: x[key])
这比第一个解决方案效率低,因为它会沿着被丢弃的方式创建列表,但我在很多地方都使用了一般原则,我首先想到了它,所以在这里。
答案 3 :(得分:0)
如果你像我一样喜欢上课,那么你可以自己上课来做这个:
from datetime import date
rows = [
{"id":"123","date":"1/1/18","foo":"bar"},
{"id":"123","date":"2/2/18", "foo":"baz"},
{"id":"456","date":"3/3/18","foo":"bar"},
{"id":"456","date":"1/1/18","foo":"bar"}
]
class unique(dict):
def __setitem__(self, key, value):
#Add key if missing or replace key if date is newer
if key not in self or self[key]["date"] < value["date"]:
dict.__setitem__(self, key, value)
data = unique() #Initialize new class based on dict
for row in rows:
d, m, y = map(int, row["date"].split('/')) #Split date into parts
row["date"] = date(y, m, d) #Replace date value
data[row["id"]] = row #Set new data. Will overwrite same ids with more recent
print data.values()
输出:
[
{'date': datetime.date(18, 2, 2), 'foo': 'baz', 'id': '123'},
{'date': datetime.date(18, 3, 3), 'foo': 'bar', 'id': '456'}
]
请注意,data
是一个dict,它基本上会覆盖使用ID作为键的__setitem__
方法。日期为date
个对象,因此可以轻松比较它们。