Не могу понять как решить задачу по Python

Есть условие задачи: Система «Контроль движения поездов» сохраняет информацию о поездах, которые шифруются именами, названиями станций прибытия и плановым временем прибытия поезда на конечную станцию. Написать программу, которая по введенному диапазону времени выводит на экран время прибытия каждого поезда в заданном диапазоне, наименование поезда, пункт его прибытия. Время прибытия поездов в заданном диапазоне расположить в порядке возрастания. Считать, что поезда прибывают в моменты времени, кратные 30 минутам. Если при вводе выходных данных следует, что, хотя бы два поезда одновременно прибывают в один пункт, то должно выводиться сообщение об аварийной ситуации и запрос на ввод новых данных.

Не могу понять, как определить то, что хотя бы два поезда одновременно прибывают в один пункт. Написал такой код, пытаясь идентифицировать значения времени по счетчикам:

from datetime import time
from collections import Counter

a = [{'train_code': '887B', 'station': 'Lipki', 'time_arrive': time(10, 00)},
     {'train_code': '411C', 'station': 'Kyiv   ', 'time_arrive': time(12, 00)},
     {'train_code': '887B', 'station': 'Lipki', 'time_arrive': time(10, 00)},
     {'train_code': '113F', 'station': 'Rivne', 'time_arrive': time(11, 30)}]

start_time = time(hour=int(input("h: ")), minute=int(input("m: ")))
end_time = time(hour=int(input("h: ")), minute=int(input("m: ")))
s = sorted(a, key=lambda x: x['time_arrive'])

for train in s:
    if start_time <= train.get('time_arrive') <= end_time:
        print(train)


s_tuple = tuple(k.get('time_arrive') for k in s)
train_counter = {k: s_tuple.count(k) for k in set(s_tuple)}
print(train_counter)

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

UPD: нужно сравнить каждый словарь в списке друг с другом по значениям station и time. Если эти значения совпадают в разных словарях, вывести в консоль сообщение об этом этом. Примечание: Есть идея создать копию списка и сравнить словари копии со словарями основного списка, но не ясно как применить это в коде. Буду благодарен за любую помощь.


Максимально приблизился к выполнению данной задачи, но столкнулся с проблемой выхода из цикла.

import pandas as pd
from datetime import time

flag = True
while flag:

  a = [{'train_code': '887B', 'station': 'Lipki', 'time_arrive': time(12, 00)},
      {'train_code': '411C', 'station': 'Kyiv   ','time_arrive': time(10, 00)},
      {'train_code': '743L', 'station': 'Lipki', 'time_arrive': time(12, 00)},
      {'train_code': '113F', 'station': 'Rivne', 'time_arrive': time(11, 00)}]

  start_time = time(hour=int(input("h: ")), minute=int(input("m: ")))
  end_time = time(hour=int(input("h: ")), minute=int(input("m: ")))
  s = sorted(a, key=lambda x: x['time_arrive'])

  for train in s:
    if start_time <= train.get('time_arrive') <= end_time:
      print(train)
      df = pd.DataFrame(a)
      res = df.groupby(["time_arrive", "station"]).filter(lambda x: len(x) > 1)
  print(res)
  print(res["time_arrive"].to_list())

  for i in res["time_arrive"].to_list():
    if start_time <= i <= end_time:
        count = len(res)
        print(count)
        if count >= 2:
            print('Error')
            break
            flag == True

        else:
            flag == False
            break

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


Ответы (3 шт):

Автор решения: MaxU

Воспользуйтесь модулем Pandas для удобной обработки и анализа табличных данных:

In [12]: import pandas as pd   #  pip install pandas

In [13]: df = pd.DataFrame(a)

Исходный DataFrame:

In [14]: df
Out[14]:
  train_code station time_arrive
0       887B   Lipki    10:00:00
1       411C    Kyiv    12:00:00
2       887B   Lipki    10:00:00
3       113F   Rivne    11:30:00

поиск строк с одинаковым временем прибытия:

In [15]: res = df.groupby("time_arrive").filter(lambda x: len(x) > 1)

результат:

In [16]: res
Out[16]:
  train_code station time_arrive
0       887B   Lipki    10:00:00
2       887B   Lipki    10:00:00
→ Ссылка
Автор решения: nc_6d

Задача решена.

import pandas as pd
from datetime import time

flag = True
while flag:

    # информация  поездах
    a = [{'train_code': '887B', 'station': 'Lipki', 'time_arrive': time(12, 00)},
         {'train_code': '411C', 'station': 'Kyiv   ', 'time_arrive': time(10, 00)},
         {'train_code': '743L', 'station': 'Lipki', 'time_arrive': time(12, 00)},
         {'train_code': '113F', 'station': 'Rivne', 'time_arrive': time(11, 00)}]

    # вводим диапазон времени приходящих поездов

    while True:
      try:
        start_time = time(hour=int(input("Enter start hours: ")), minute=int(input("Enter start minutes: ")))
        end_time = time(hour=int(input("Enter last hours: ")), minute=int(input("Enter last minutes: ")))
        break
      except ValueError:
        print('Enter an integers!')
    s = sorted(a, key=lambda x: x['time_arrive'])

    # проверяем прибытие каких поездов совпадает с заданым нами диапазоном

    for train in s:
        if start_time <= train.get('time_arrive') <= end_time:
            print(train)
            df = pd.DataFrame(a)
            res = df.groupby(["time_arrive", "station"]).filter(lambda x: len(x) > 1)
            count = len(res)

    # если среди маршрутов есть такие, что приходят на одну станцию в одно и то же время, выводим ошибку и просим ввести новые данные

    for i in res["time_arrive"].to_list():

        if (start_time <= i <= end_time) and (count >= 2):
                print(f'There are {count} wrong destinations')
                print('Accident error. Choose the other time range please.')
                break

            # если "ошибочных маршрутов" нет, прекращаем работу программы

        else:
                flag = False
                break

→ Ссылка
Автор решения: MiniMax

В начале замечание по вашему коду:

a = [{'train_code': '887B', 'station': 'Lipki', 'time_arrive': time(12, 00)},
      {'train_code': '411C', 'station': 'Kyiv   ','time_arrive': time(10, 00)},
      {'train_code': '743L', 'station': 'Lipki', 'time_arrive': time(12, 00)},
      {'train_code': '113F', 'station': 'Rivne', 'time_arrive': time(11, 00)}]

Неправильно добавлять преобразование времени прибытия типа time(12, 00) в каждую запись вручную. Количество записей может меняться - сейчас у вас 4 записи, а может быть и 40. Также возможна ситуация, когда записи читаются из файла или из базы данных. Список должен содержать только данные, преобразование нужно делать динамически - либо изменяя этот список, либо создавая новый.

Я разбил решение задачи на три шага:

  1. Прохожусь по исходному списку с поездами, извлекая из него имя станции и время прибытия поезда и формирую из этих данных таблицу прибытия типа: словарь[время][станция] = [{поезд_1}, {поезд_2}, {поезд_3}, ...]. Таким образом, если словарь['12:30']['Kyiv'] имеет список длиной больше единицы, то это означает совпадение времени прибытия двух или более поездов на эту станцию. При желании, уже на этом шаге можно выдавать сообщение об ошибке.

  2. Получив от пользователя временной диапазон, перебираю все подходящие варианты времени прибытия. Для каждого варианта перебираю станции попадающие на это время. Если станция имеет 1 поезд - печатаю, иначе сообщение об ошибке.

  3. Если пункт 2 отработал без ошибок, то завершение работы программы, иначе повтор пункта 2.

Можно сделать, чтобы программа не печатала ни одного поезда при наличии пересечения поездов, а просто запрашивала новый временной диапазон.

Или наоборот, чтобы программа сначала печатала все поезда, включая пересекающиеся, потом выдавала сообщение об ошибке.

У меня сделан промежуточный вариант: печатаются только поезда до первого пересечения, при наличии которого выдаётся ошибка и происходит запрос нового диапазона.

Решение

#!/usr/bin/python3

from datetime import timedelta, datetime
from collections import defaultdict

def get_time():
    # функция datetime() кроме времени требует год, месяц, день, поэтому
    # передаём произвольные - в качестве заглушки.
    return datetime(2001,1,1, hour=int(input("h: ")), minute=int(input("m: ")))

def print_trains(arrival_table):
    start_time = get_time()
    end_time = get_time()
    # Устанавливаем временной интервал для проверки расписания
    step = timedelta(minutes=30)

    cur_time = start_time
    while cur_time <= end_time:
        if cur_time in arrival_table:
            # Перебираем все поезда приходящие в указанное время.
            # Если в указанное время на станцию приходит больше одного
            # поезда, то печатаем сообщение об ошибке и
            # завершаем работу функции, иначе печатаем информацию о поезде.
            for station, train_lst in arrival_table[cur_time].items():
                print(cur_time.time())
                if len(train_lst) > 1:
                    print(f"Emergency situation on station {station}!")
                    print("Choose another time range.")
                    return 0
                else:
                    print(train_lst[0])

                print()
        # Увеличиваем время
        cur_time += step
    return 1

def make_arrival_table(trains):
    for record in trains:
        # Преобразуем время из строчного типа в datetime.
        # Год, месяц, день добавляем произвольные.
        record['time_arrive'] = datetime.strptime('2001 1 1 ' + record['time_arrive'], '%Y %m %d %H:%M') 
    # defaultdict нужен для избавления от проверок на существование
    # требуемого ключа. При отсутствии ключ создаётся автоматически.
    arrival_table = defaultdict(lambda: defaultdict(list))

    for entry in trains:
        arrival_table[entry['time_arrive']][entry['station']].append(entry)

    return arrival_table
# поле 'time_arrive' изменено на строчный тип 
trains = [
            {'train_code': '887B', 'station': 'Lipki', 'time_arrive': '10:00'},
            {'train_code': '411C', 'station': 'Kyiv', 'time_arrive': '12:00'},
            {'train_code': '887B', 'station': 'Lipki', 'time_arrive': '10:00'},
            {'train_code': '113F', 'station': 'Rivne', 'time_arrive': '11:30'}
        ]

arrival_table = make_arrival_table(trains)
# Цикл будет повторяться, пока не будет найден интервал
# без пересекающихся поездов
while not print_trains(arrival_table):
    pass
→ Ссылка