Создать subdict из основного dict

#python #dictionary #iteration

#python #словарь #итерация

Вопрос:

У меня есть словарь строки временных диапазонов, например:

 {'10:00-12:00': 'Maths',
 '13:00-15:00': 'Physics',
 '16:00-18:00': 'History',
 '19:00-22:00': 'Biology',
 '23:00-01:00': 'Chemistry',
 '02:00-04:00': 'Computer',
 '05:00-10:00': 'English'}
 

Теперь я хочу извлечь время окончания, например 12:00 , и 13:00 из словаря и создать словарь с именем второго субъекта.

Итак, результат, который я хочу, это:

 {'12:00-13:00':'Physics',
 '15:00-16:00':'History',
 '18:00-19:00':'Biology',
 '22:00-23:00':'Chemistry',
 '01:00-02:00':'Computer',
 '04:00-05:00':'English'}
 

Я мог бы просто создать этот словарь сам (путем жесткой настройки значений), но я хотел, чтобы он был динамическим, потому что ключи — это имя столбца фрейма данных (из csv), а значения — это имя поля. Поэтому я хотел, чтобы python динамически подбирал его на основе csv. Я пробовал использовать zip(lst,lst[1:]) , но он не работает со словарем, поскольку индексация отличается для словаря.
Я также пытался использовать:

 data = {'10:00-12:00': 'Maths',
        '13:00-15:00': 'Physics',
        '16:00-18:00': 'History',
        '19:00-22:00': 'Biology',
        '23:00-01:00': 'Chemistry',
        '02:00-04:00': 'Computer',
        '05:00-10:00': 'English'}

def pairwise(iterable):
    it = iter(iterable)
    a = next(it, None)

    for b in it:
        yield (a, b)
        a = b

a = pairwise(data)
print(list(a)) #[('10:00-12:00', '13:00-15:00'), ('13:00-15:00', '16:00-18:00'), ('16:00-18:00', '19:00-22:00'), ('19:00-22:00', '23:00-01:00'), ('23:00-01:00', '02:00-04:00'), ('02:00-04:00', '05:00-10:00')]
#not the keys of the desired output
 

Спасибо!

Комментарии:

1. Вам лучше сначала проанализировать диапазоны времени даты в надлежащий тип данных, например datetime , а затем выполнить манипуляции. Игра с временными строками приведет только к бесчисленным головным болям.

2. @rdas На самом деле позже я перебираю dict, делая их datetime.

3. @PatrickArtner Я отредактировал его, теперь стало лучше?

4. Просто для ясности: ваша цель — рассчитать паузу перед сеансом (математика … -12:00, а физика 13:00-… => 12:00-13:00) или просто, чтобы получить час до сеанса (физика — это 13:00-… => 12:00-13:00)? Поскольку все паузы для этих данных длятся один час, оба подхода дают одинаковые результаты.

5. @MisterMiyagi На самом деле да, разница во времени между математикой и физикой равна 1, но она также может увеличиваться или уменьшаться, то есть я пытаюсь проверить, находится ли текущее время между этими 12:00 и 13:00, поэтому для этого я хотел извлечь 12:00 и 13:00. 12:00и 13:00 также может быть что-то вроде 12:00 и 13:30.

Ответ №1:

 import more_itertools
from collections import namedtuple

data = {'10:00-12:00': 'Maths', '13:00-15:00': 'Physics',
        '16:00-18:00': 'History', '19:00-22:00': 'Biology', 
        '23:00-01:00': 'Chemistry', '02:00-04:00': 'Computer', 
        '05:00-10:00': 'English'}

Item = namedtuple('Item', ('start', 'end', 'subject'))
def parse(item):
    times, subject = item
    start, end = times.split('-')
    return Item(start, end, subject)

def generate_items(data):
    for item1, item2 in more_itertools.windowed(data,2):
        yield f'{item1.end}-{item2.start}', item2.subject

data = [parse(item) for item in data.items()]
print(dict(generate_items(data)))
 

вывод

 {'12:00-13:00': 'Physics', '15:00-16:00': 'History', '18:00-19:00': 'Biology', '22:00-23:00': 'Chemistry', '01:00-02:00': 'Computer', '04:00-05:00': 'English'}
 

подробнее-пакет iterools удобен, но more_itertools.windowed() его можно заменить чем-то вроде zip(data[:-1], data[1:])

 from collections import namedtuple

data = {'10:00-12:00': 'Maths', '13:00-15:00': 'Physics',
        '16:00-18:00': 'History', '19:00-22:00': 'Biology', 
        '23:00-01:00': 'Chemistry', '02:00-04:00': 'Computer', 
        '05:00-10:00': 'English'}


Item = namedtuple('Item', ('start', 'end', 'subject'))
def parse(item):
    times, subject = item
    start, end = times.split('-')
    return Item(start, end, subject)

def generate_items(data):
    for item1, item2 in zip(data[:-1], data[1:]):
        yield f'{item1.end}-{item2.start}', item2.subject

data = [parse(item) for item in data.items()]
print(dict(generate_items(data)))
 

Комментарии:

1. more_itertools ? аккуратно.

2. Это решение также является динамическим, понятия не имею, что происходит, но это работает здесь. Спасибо!

Ответ №2:

Это даст вам то, что вы хотите

 a = {'10:00-12:00': 'Maths', '13:00-15:00': 'Physics', '16:00-18:00': 'History', '19:00-22:00': 'Biology', '23:00-01:00': 'Chemistry', '02:00-04:00': 'Computer', '05:00-10:00': 'English'}

keys_split = list(map(lambda s: s.split("-"), a.keys()))
flatten = lambda l: [item for sublist in l for item in sublist]
ks = flatten(keys_split)
keys_tmp= list(zip(ks[1::2],ks[2::2]))
keys_res = list(map(lambda e: e[0] '-' e[1], keys_tmp))
values_res = list(a.values())[1:]
dict_res = dict(zip(keys_res, values_res))
 

и при печати будет выглядеть так

 print(dict_res)
#{'12:00-13:00': 'Physics', '15:00-16:00': 'History', '18:00-19:00': 'Biology', '22:00-23:00': 'Chemistry', '01:00-02:00': 'Computer', '04:00-05:00': 'English'}
 

объяснение того, что происходит:

  • строка 1: a.keys() предоставит вам все ключи, каждый ключ представляет собой строку
  • строка 1: map принимает лямбда-функцию, которая перебирает все строки, которые мы видели в a.keys(), и разделяет их на ‘-‘
 print(a.keys())
# dict_keys(['10:00-12:00', '13:00-15:00', '16:00-18:00', '19:00-22:00', '23:00-01:00', '02:00-04:00', '05:00-10:00'])
print(keys_split)
# [['10:00', '12:00'], ['13:00', '15:00'], ['16:00', '18:00'], ['19:00', '22:00'], ['23:00', '01:00'], ['02:00', '04:00'], ['05:00', '10:00']]
 
  • строка 2: flatten — это лямбда-функция, которая выполняет итерацию по списку l и выравнивает список списков до простого списка
  • строка 3: примените flatten к keys_split, чтобы получить плоский список
 print(ks)
# ['10:00', '12:00', '13:00', '15:00', '16:00', '18:00', '19:00', '22:00', '23:00', '01:00', '02:00', '04:00', '05:00', '10:00']
 
  • строка 4: создайте временный список с помощью zip поверх списка 1 (ks [1::2]) с новыми метками времени начала 12:00, 15:00, 16:00 и т.д. и над списком 2 (k2[2:: 2]) с новыми конечными временными метками 13:00,16:00, 19:00 и т.д.
  • строка 5: чтобы получить ключи в нужном формате, сопоставьте все элементы временного списка, чтобы объединить элементы с ‘-‘ между ними, например ’12:00-13:00′, ’15:00-16:00’ и т.д.
 print(keys_tmp)
#[('12:00', '13:00'), ('15:00', '16:00'), ('18:00', '19:00'), ('22:00', '23:00'), ('01:00', '02:00'), ('04:00', '05:00')]
print(keys_res)
#['12:00-13:00', '15:00-16:00', '18:00-19:00', '22:00-23:00', '01:00-02:00', '04:00-05:00']
 
  • строка 6: получить все значения из dict a, но не учитывать первый элемент
  • строка 7: окончательный dict_res — это zip-файл над двумя списками keys_res и values_res
 print(values_res)
#['Physics', 'History', 'Biology', 'Chemistry', 'Computer', 'English']
 

Надеюсь, это немного прояснит ситуацию, просто перейдите построчно и распечатайте, чтобы посмотреть, что происходит 🙂

Комментарии:

1. У вас там опечатка в строке 5, keys_split

2. Спасибо! Это тоже работает динамически, хотя я понятия не имею, что происходит, возможно, включите немного информации о том, что здесь происходит.

3. да, конечно, теперь будет добавлена информация, также, если вы распечатаете переменные, это также поможет в том, что происходит

4. Я думаю, что создание функции из этого и передача словаря в качестве аргумента лучше, верно?

5. Да, если вам нужно снова применить эту логику к другим dicts, имеет смысл определить ее как функцию

Ответ №3:

Вероятно, это проще сделать, используя время преобразования datetime.time, но вы можете сделать это на основе чистой строки без какого-либо импорта :

 s = {'10:00-12:00': 'Maths',      '13:00-15:00': 'Physics',
     '16:00-18:00': 'History',    '19:00-22:00': 'Biology',
     '23:00-01:00': 'Chemistry',  '02:00-04:00': 'Computer',
     '05:00-10:00': 'English'}

# this is very explicit - you can reduce the amount of variables used
# by making the comprehensions bigger - but for SO I prefer readability 

# extract end times and convert to int
times = [ tuple(map(int, (t.split("-")[1]).split(":"))) for t in s.keys()][:-1]

# get lectures
what = list(s.values())[1:]
print(times, what, sep="n") 

# create new schedule with end times
new_sched= [f"{v1:02d}:{v2:02d}-{v1 1:02d}:{v2:02d}" for (v1,v2) in times]

# construct new scheduling
m = {}
for when,wh in zip(new_sched,what):
    m[when] = wh

# check 
print(m)
print( {'12:00-13:00':'Physics', '15:00-16:00':'History',
'18:00-19:00':'Biology', '22:00-23:00':'Chemistry',
'01:00-02:00':'Computer', '04:00-05:00':'English'})
 

Вывод:

 # parsed end times as tuple of integers
[(12, 0), (15, 0), (18, 0), (22, 0), (1, 0), (4, 0)]
# extracted lectures
['Physics', 'History', 'Biology', 'Chemistry', 'Computer', 'English']

# constructed dict
{'12:00-13:00': 'Physics', '15:00-16:00': 'History', 
 '18:00-19:00': 'Biology', '22:00-23:00': 'Chemistry', 
 '01:00-02:00': 'Computer', '04:00-05:00': 'English'}
# given result dict
{'12:00-13:00': 'Physics', '15:00-16:00': 'History', 
 '18:00-19:00': 'Biology', '22:00-23:00': 'Chemistry', 
 '01:00-02:00': 'Computer', '04:00-05:00': 'English'} 
 

Комментарии:

1. Он работал с начальным словарем, но когда я изменил csv (следовательно, изменил свой словарь), я получаю тот же результат, что и начальный dict, до

2. @Cool не уверен, что ты имеешь в виду. Я отредактировал, чтобы также включить часть протокола как часть решения.

3. @Cool В качестве времени начала физики используется время окончания математики, все новые лекции длятся 1 час. Он использует старое время начала физики для истории, а также имеет длину 1 час и т.д.

4. Он работает с этой логикой, но я попытался изменить временной интервал, и это не сработало, так что было любопытно, в любом случае, спасибо за этот ответ с 0 импортами, отлично! Я смог лучше понять понимание вложенного списка.