Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Васильев Ю. - Python для data science (Библиотека программиста) - 2023.pdf
Скачиваний:
6
Добавлен:
07.04.2024
Размер:
7.21 Mб
Скачать

Объединение встроенных структур данных      153

orders_details = [[o for o in orders if d[0] == o[0]][0] + d[1:] for d in details]

Во внешнем списковом включении мы выполняем итерацию по кортежам списка details. Во внутреннем — находим в списке orders кортеж, номер заказа которого совпадает с текущим кортежем из details. Поскольку строке заказа из details должен соответствовать только один кортеж из orders, внутреннее списковое включение должно генерировать список с одним элементом (кортежем, представляющим заказ). Итак, берем первый элемент внутреннего спискового включения с помощью оператора [0], а затем конкатенируем кортеж этого заказа с соответствующим кортежем из details с помощью оператора +, исключая лишний номер заказа ([1:]).

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

[

(9423517, '2022-02-04', 9001, 'Jeans', 'Rip Curl', 87.0, 1), (9423517, '2022-02-04', 9001, 'Jacket', 'The North Face', 112.0, 1), (4626232, '2022-02-04', 9003, 'Socks', 'Vans', 15.0, 1),

(4626232, '2022-02-04', 9003, 'Jeans', 'Quiksilver', 82.0, 1), (9423534, '2022-02-04', 9001, 'Socks', 'DC', 10.0, 2), (9423534, '2022-02-04', 9001, 'Socks', 'Quiksilver', 12.0, 2), (9423679, '2022-02-05', 9002, 'T-shirt', 'Patagonia', 35.0, 1), (4626377, '2022-02-05', 9003, 'Hoody', 'Animal', 44.0, 1),

(4626377, '2022-02-05', 9003, 'Cargo Shorts', 'Animal', 38.0, 1), (4626412, '2022-02-05', 9004, 'Shirt', 'Volcom', 78.0, 1), (9423783, '2022-02-06', 9002, 'Boxer Shorts', 'Superdry', 30.0, 2), (9423783, '2022-02-06', 9002, 'Shorts', 'Globe', 26.0, 1), (4626490, '2022-02-06', 9004, 'Cargo Shorts', 'Billabong', 54.0, 1), (4626490, '2022-02-06', 9004, 'Sweater', 'Dickies', 56.0, 1)

]

Список содержит все кортежи из details, и каждый кортеж также содержит дополнительную информацию из соответствующего кортежа списка orders.

Реализация join-объединений списков

Операция, которую мы выполнили в предыдущем разделе, является стандартным объединением типа «один-ко-многим»: каждая строка заказа в details имеет соответствующий заказ в orders, а каждый заказ в orders — одну или несколько строк в details. Однако на практике любой из объединяемых датасетов

154      Глава 7. Объединение датасетов

(или оба) может содержать строки, не имеющие совпадений в другом. Чтобы учесть такие ситуации, необходимо научиться выполнять операции объединения, эквивалентные тем, что используются в базах данных (мы обсуждали их в главе 3): left join, right join, inner join и outer join.

Например, список details может содержать строки заказов, которых нет в списке orders. Это может произойти, если orders фильтруется по определенному диапазону дат; поскольку details не содержит поля с датой, невозможно отфильтровать этот список соответствующим образом. Чтобы смоделировать эту ситуацию, добавьте в details строку с заказом, которого нет в orders:

details.append((4626592, 'Shorts', 'Protest', 48.0, 1))

Теперь попытаемся сгенерировать список orders_details, как мы делали раньше:

orders_details = [[o for o in orders if d[0] == o][0] + d[1:] for d in details]

Получаем ошибку:

IndexError: list index out of range

Проблема возникает, когда мы добираемся до номера заказа в списке details, для которого нет совпадений, и пытаемся получить первый элемент соответствующего внутреннего спискового включения. Такого элемента не существует, поскольку номер заказа отсутствует в списке orders. Один из способов решения этой проблемы — добавление блока if в цикл for d in details внутри внешнего спискового включения, проверяющего, есть ли номер заказа из details в какойто из строк orders:

orders_details = [[o for o in orders if d[0] in o][0] + d[1:] for d in details

if d[0] in [o[0] for o in orders ]]

Вы устраните проблему, исключив все строки details, не имеющие совпадений в orders, с помощью проверки в блоке if , который следует за циклом for d in details. Таким образом, это списковое включение соответствует объединению типа inner join.

Но что, если требуется включить все строки details в итоговый список orders_ details, например, для нахождения итоговой суммы всех заказов, а не только заказов из текущего списка orders (который гипотетически был отфильтрован

Объединение встроенных структур данных      155

по дате)? Затем можно найти итоговую сумму заказов из текущего списка orders и сравнить полученные значения.

В таком случае необходимо реализовать right join, предполагая, что список orders находится в левой части «отношений», а список details — в правой. Напомним, что объединение типа right join возвращает все строки из правого датасета и только совпадающие строки из левого. Обновим предыдущее списковое включение:

orders_details_right = [[o for o in orders if d[0] in o][0] + d[1:] if d[0] in

[o[0] for o

in orders] else (d[0], None, None) + d[1:] for d in

details]

Здесь мы добавляем к условию if блок else , который находится в теле цикла for d in details. Этот блок else будет срабатывать для каждой строки из details, не имеющей совпадений в orders. else создает новый кортеж, содержащий номер заказа и две записи None на месте отсутствующих в orders полей, и объединяет этот кортеж со строкой из details, получая строку со структурой, подобной структуре остальных строк. Таким образом, в дополнение ко всем строкам, которые есть в обоих списках, созданный набор данных будет содержать строку из details, для которой нет соответствия в orders:

[

--фрагмент--

(4626490, '2022-02-06', 9004, 'Sweater', 'Dickies', 56.0, 1), (4626592, None, None, 'Shorts', 'Protest', 48.0, 1)

]

Теперь, когда у нас есть список orders_details_right (объединение списков orders и details методом right join), можно суммировать все заказы и сравнить результаты с суммой только тех заказов, которые есть в списке orders. Вычисляем общую сумму всех заказов с помощью встроенной в Python функции sum():

sum(pr*qt for _, _, _, _, _, pr, qt in orders_details_right)

Цикл for, передаваемый в качестве параметра в sum(), немного похож на цикл, используемый в списковом включении, поскольку позволяет брать только нужные элементы на каждой итерации цикла. В данном конкретном примере все, что нам нужно найти на каждой итерации, — это pr*qt, произведение значений цены (Price) и количества (Quantity) из имеющегося кортежа. Поскольку другие