def one():
    return """
#Организация массивов в NumPy: хранение данных, создание массивов, принципы реализации операций с едиными исходными данными 
Для того чтобы использовать библиотеку NumPy, её нужно правильно импортировать: import NumPy as np. Массивы в NumPy можно создать таким образом: a = np.array([1,2,3…]). Однако важно осознавать, что данные, находящиеся внутри массива, всегда будут однородны (одного типа), а также размер самого массива должен соответствовать размеру матрицы.

Атрибуты массива:
ndarray.shape: размерности массива.
ndarray.dtype: тип данных элементов массива.
ndarray.ndim: количество измерений (осей) массива.
ndarray.size: общее количество элементов в массиве.
ndarray.itemsize: размер одного элемента массива в байтах.
ndarray.nbytes: общее количество байт, занимаемое массивом.

#Универсальные функции и применение функций по осям в NumPy 
Универсальные функции — это функции, которые выполняют поэлементные операции над объектами NumPy. Эти функции оптимизированы для работы с массивами и обычно работают намного быстрее, чем эквивалентные функции, реализованные в чистом Python.
Основные универсальные функции:
    операции сравнения: <, <=, ==, !=, >=, >
    арифметические операции: +, -, *, /, %, reciprocal, square
    экспоненциальные функции: exp, expm1, exp2, log, log10, log1p, log2, power, sqrt
    тригонометрические функции: sin, cos, tan, acsin, arccos, atctan
    гиперболические функции: sinh, cosh, tanh, acsinh, arccosh, atctanh
    побитовые операции: &, |, ~, ^, left_shift, right_shift
    логические операции: and, logical_xor, not, or
    предикаты: isfinite, isinf, isnan, signbit
    другие функции: abs, ceil, floor, mod, modf, round, sinc, sign, trunc

Применение функций по осям (axis)
В NumPy массивы могут быть многомерными, и часто необходимо выполнять операции по конкретным осям этих массивов. Параметр axis в функциях NumPy позволяет контролировать, по какой оси будет выполняться операция.
Основные концепции:
·         Ось (axis): В многомерных массивах оси индексируются от 0 до N-1, где N — количество измерений. Ось 0 соответствует первому измерению (строкам), ось 1 — второму (столбцам) и т.д.
·         Функции с параметром axis: Многие функции в NumPy принимают параметр axis, который указывает, вдоль какой оси должна выполняться операция.


⦁	Принцип распространения значений при выполнении операций в NumPy: общий алгоритм и примеры 
В качестве аргументов универсальных функций могут быть массивы с различной, но сравнимой формой. В этом случае применяется механизм распространения (broadcsting).
Правила выполнения распространения:
⦁	 соответствующие измерения двух массивов должны либо совпадать
⦁	либо одно из них должно быть равно единице.
Если эти условия не выполняются, выдается исключение ValueError: operands could not be broadcast together, указывающее, что массивы имеют несовместимые формы.
Если в одном из массивов не хватает измерений, то считается, что недостающее количество измерений — это младшие измерения (измерения с наименьшими номерами), которым приписывается размерность 1. 

⦁	Векторизация в numpy: ключевые параметры функции, примеры применения, использование обобщенной сигнатуры функции
Векторизация для скалярных функций NumPy
Векторизация позволяет записывать применение функции для преобразования множества значений (вектора) за одну операцию. 

numpy.vectorize - это класс обобщенных функций, который позволяет создавать векторизованные функции в NumPy.
numpy.vectorize позволяет определять векторизованные функции которые принимают массивы NumPy (или вложенные последовательности объектов) и возвращают массивы NumPy (единичные или кортежи).
Конструктор класса выглядит следующим образом: class numpy.vectorize(pyfunc, otypes=None, doc=None, excluded=None, cache=False, signature=None)
Ключевым аргументом является функция pyfunc - функция, которую требуется векторизовать.

В результате применения конструктора numpy.vectorize появляется вызываемый (callable) объект типа numpy.vectorize , по сути это есть векторизованная функция.
Векторизованная функция вызывает функцию pyfunc для элементов входных массиов аналогично функции map в Python, при этом применяются правила распространения (broadcasting) NumPy. 
Векторизация для векторных функций в NumPy
В результате векторизации векторные функции могут эффективно (в смысле компактности записи и эффективности вычислений) применяться для массивов больших размерностей.
Для реализации этого механизма конструктору numpy.vectorize необходимо передать информацию о том какая векторная структура у входных параметров и выходных значений. Это делается с помощью передачи обобщенной сигнатуры функции через параметр signature.
Обобщенная сигнатура функции (generalized ufunc signature) определяет как размерности каждого из входных/выходных массивов разбиваются на размерности относящиеся к ядру (т.е. становятся параметрами единичного вызова векторизуемой функции pyfunc ) и на размерности, использующиеся для векторизации.
Основные правила:
каждое измерение в сигнатуре соотносится с измерениями соответствующих передаваемых массивов (соответствие строится начиная с конца кортежа, определяющего форму (shape) передаваемого массива).
Измерения ядра, которым присвоены одинаковые имена, должны точно совпадать по размерам, в этом случае распространение (bradcasting) не производится.
При применении векторизации измерения ядра убираются из всех входов, а для остающиеся измерений выполняется бродкастинг для выполнения итераций по ним в рамках работы векторизации.

⦁	Numba: принципы работы, базовые примеры использования 
Numba - JIT компилятор с открытым исходным кодом, который компилирует подмножество кода Python и NumPy в быстрый машинный код. 

⦁	Numba спроектирована для работы с массивами и функциями NumPy.
⦁	Numba генерирует специализированный код для различных типов массивов и их размещения для оптимизации производительности.
⦁	Специализированные декораторы могут созвать ufunc которые могут использоваться для распространения по массивам NumPy, так же как это делают ufunc NumPy.
⦁	Numba хорошо интегрирована с работой Jupyter notebooks для обеспечения интерактивных вычислений и с распеределнными вычислительными средами, такими как Dask и Spark.
Как работает Numba:
⦁	Читает байт код Python для декорированной функции.
⦁	Собирает информацию о типах входных аргументов функций.
⦁	Анализирует и оптимизирует код.
⦁	Использует библиотеку для компиляции LLVM для генерации машинного кода функции для конкретного CPU.
⦁	Данный машинный код используется каждый раз при вызове данной функции (с аргументами того же типа).
Когда Numba даст хороший прирост производительности:
Если код ориентирован на численные операции, в т.ч.:
⦁	активно использует NumPy
⦁	имеется много циклов (большое количество итераций)

Примеры использования декоратора @jit 
Когда вы помечаете функцию декоратором @jit, Numba анализирует вашу функцию и определяет, как её можно оптимизировать. Она смотрит на типы переменных и операции, которые вы выполняете, и на основе этого создает оптимизированный машинный код. Этот процесс происходит во время первого вызова функции, так что первый запуск может занять чуть больше времени, но все последующие вызовы будут быстрыми.


⦁	Векторизация SIMD (Single Instruction, Multiple Data) позволяет выполнять одну и ту же операцию над множеством данных одновременно

Чтобы получить байт-код для функции, декорированной с помощью Numba используем модуль inspect (позволяет получить доступ к метаданным функций, включая их байт-код). Однако, стоит отметить, что байт-код, полученный через inspect, будет отличаться от того, который генерируется непосредственно Numba во время JIT-компиляции, поскольку Numba добавляет дополнительные оптимизации и изменения в процессе компиляции.
Как получить байт-код для функции add_arrays:

⦁	Организация Pandas DataFrame и организация индексации для DataFrame и Series 
DataFrame - аналог двухмерного массива с гибкими индексами строк и гибкими именами столбцов. Объект DataFrame можно рассматривать как упорядоченную последовательность выровненных объектов Series. (Объект Series - одномерный массив индексированных данных). Под «выравниванием» понимается то, что они используют один и тот же индекс.

Серии поддерживают механизмы индексации, аналогичные массивам NumPy: срезы, маскирование и прихотливое индексирование.

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


⦁	Применение универсальных функций и работа с пустыми значениями в Pandas 
Все универсальные функции библиотеки NumPy работают с объектами Series и DataFrame библиотеки Pandas.
Результатом применения универсальной функции NumPy к объектам Pandas будет новый объект с сохранением индексов


При бинарных операциях над двумя объектами Series или DataFrame библиотека Pandas будет выравнивать индексы в процессе выполнения операции. Получившийся в итоге массив содержит объединение индексов двух исходных массивов. Недостающие значения будут отмечены как NaN («нечисловое значение»), с помощью которого библиотека Pandas отмечает пропущенные данные. 


⦁	Специфика текстовых и бинарных файлов, форматы файлов CSV и Pickle, представление данных в этих форматах и взаимодействие с ними в Python
Форматы файлов можно разделить по способу организаци хранения данных на:
·        Текстовые файлы - файл, содержащий текстовые данные, представленные как последовательность символов (в основном печатных знаков, принадлежащих к определенному набору символов - кодовой таблице (кодировке: ASCII, Unicode)). Эти символы обычно сгруппированы в строки (lines, rows). Эти файлы обычно имеют расширения .txt, .csv, .json и др.
·        Двоичные (бинарные) файлы (binary file) - в узком смысле "не текстовые файлы". Примеры включают .bin, .exe, .pickle и другие.
CSV — текстовый формат, использующий запятые для разделения значений. Этот формат часто используется для представления табличных данных (например, данных из Excel).

Pickle — бинарный формат, используемый для сериализации и десериализации объектов Python. Этот формат позволяет сохранить сложные структуры данных (например, списки, словари, объекты) и затем восстановить их. Не рекомендуется для работы с недоверенными данными, так как может выполнить произвольный код при десериализации.

⦁	Задача сериализации и десериализации, описание формата файла JSON и пример описания данных в этом формате и взаимодействия с ним в Python 
Сериализация (serialization) - процесс перевода какой-либо структуры данных в последовательность битов.
 
Обратной к сериализации является операция десериализации (deserialization) — восстановление начального состояния структуры данных из битовой последовательности.
Основные задачи сериализации и десериализации:
⦁	Хранение данных: Сохранение состояния объекта для последующего использования.
⦁	Передача данных: Отправка данных между различными системами или процессами.
⦁	Кэширование: Быстрое сохранение и восстановление промежуточных результатов.
JSON (JavaScript Object Notation) — это текстовый формат для обмена данными, основанный на синтаксисе JavaScript. Однако JSON используется независимо от JavaScript и поддерживается в большинстве языков программирования.
Основные свойства JSON:
⦁	Легкость чтения и записи: Читаемый формат с простой структурой.
⦁	Язык-независимость: Широко поддерживается различными языками программирования.
⦁	Гибкость: Поддерживает сложные структуры данных, включая объекты и массивы.
 
Пример описания данных в формате JSON
JSON-данные описываются с помощью пар "ключ-значение". Пример JSON-файла, описывающего информацию о пользователе:


Взаимодействие с JSON в Python
Для работы с JSON в Python используется стандартный модуль json. Рассмотрим процесс записи данных в JSON-файл и чтения данных из JSON-файла:


⦁	Формат XML и модель DOM: общая характеристика, пример описания данных в XML и DOM, работа с ними с помощью библиотеки BeautifulSoup 
DOM— это не зависящий от платформы и языка программный интерфейс, позволяющий программам и скриптам получить доступ к содержимому HTML-, XHTML- и XML-документов, а также изменять содержимое, структуру и оформление таких документов. Любой документ известной структуры с помощью DOM может быть представлен в виде дерева узлов. Узлы связаны между собой отношениями «родительский-дочерний».
XML - расширяемый язык разметки. XML разрабатывался как язык с простым формальным синтаксисом, удобный для создания и обработки документов программами и одновременно удобный для чтения и создания документов человеком, с подчёркиванием нацеленности на использование в Интернете.

Цели создания:
⦁	Эффективное описание данных.
⦁	Обмен данными между информационными системами 
Основные особенности:
⦁	Простой синтаксис.
⦁	Удобство чтения и создания документов человеком.
⦁	Удобство обмена документами в интернете.
⦁	Гибкость применения и расширяемость 
⦁	Очень широкая распространенность и доступность инструментов.

Работа с XML и DOM с помощью библиотеки BeautifulSoup включает следующие основные шаги:
⦁	Парсинг XML:
⦁	Навигация по DOM:
⦁	Поиск элементов:
⦁	Извлечение данных:
⦁	Модификация данных:
⦁	Пример работы:.
Использование BeautifulSoup упрощает манипуляции с XML-данными, делая процесс парсинга, навигации и модификации более интуитивно понятным и удобным для разработчика.



⦁	Взаимодействие с Excel из Python с помощью XLWings: принципы работы и примеры использования
xlwings — это библиотека для автоматизации Excel с помощью Python вместо VBA.
VBA - Удобная оболочка для pywin32. 
Вы можете вызывать Python из Excel и наоборот и писать пользовательские функции в Python 
xlwings — это библиотека с открытым исходным кодом 
Возможности xlwings:
Взаимодействие / автоматизация - позволяет: 
Использовать научный стек Python для интерактивного анализа данных с помощью Jupyter Notebooks, NumPy, Pandas, scikit-learn и т.д. 
Макросы и инструменты Excel - позволяет создавать инструменты для Excel с помощью Python вместо VBA и вызывать свой код непосредственно из Excel, например, с помощью кнопки на листе. Это также отлично подходит для прототипирования веб-приложений.
Определяемые пользователем функции - позволяет легко писать пользовательские функции в Excel (UDF) используя все функции, уже доступные в библиотеках, таких как NumPy и Pandas. 
REST API - Выставляйте свои книги Excel через REST API и получайте к ним доступ с других компьютеров.

⦁	Основы работы с регулярными выражениями: базовый синтаксис, примеры использования модуля re в Python 
Синтаксис регулярных выражений
Внутри регулярного выражения символы. ^ $ * + ? { } [ ] \ | ( ) - имеют специальное значение.
Экранирование с помощью слэша \
Некоторые специальные символы теряют свое особое значение, если их разместить внутри квадратных скобок []. В этом случае экранировать их не нужно.
⦁	{n} - n вхождений символа в строку. Например. шаблон r"[0-9]{2}" соответствует двум вхождениям любой цифры
⦁	{n,} - n или более вхождений символа в строку. Например. шаблон r"[0-9]{2,}" соответствует двум и более вхождениям любой цифры
⦁	{n,m} - не менее n и не более m вхождений символа в строку. Числа указываются через запятую без пробела.
⦁	Например, шаблон r"[0-9]{2,4}" соответствует от двух до четырех вхождениям любой цифры
⦁	* - ноль или большее число вхождений символа в строку. Эквивалентно комбинации {0,}
⦁	+ - одно или большее число вхождений символа в строку. Эквивалентно комбинации {1,}
⦁	? - ни одного или одно вхождение символа в строку. Эквивалентно комбинации {0,1}.

⦁	^ - привязка к началу строки или подстроки. Зависит от модификаторов M (или MULTILINE) и S (или DOTALL)
⦁	$ - привязка к концу строки или подстроки. Зависит от модификаторов M (или MULTILINE) и S (или DOTALL)
⦁	\А - привязка к началу строки (не зависит от модификатора)
⦁	\Z - привязка к концу строки (не зависит от модификатора).
(модификаторы рассматриваются ниже)

Основные методы

re.match() - Этот метод ищет по заданному шаблону в начале строки
re.search() - Этот метод ищет по заданному шаблону во всей строке. Возвращает первое вхождение 

re.findall() - Этот метод возвращает список всех найденных совпадений

re.split() - Этот метод разделяет строку по заданному шаблону. Первый аргумент функции - регулярное выражение, обозначающее разделитель, второй аргумент - исходная строка

re.sub() - Этот метод ищет шаблон в строке и заменяет его на указанную подстроку

re.compile() - собирает регулярное выражение в отдельный объект, который может быть использован для поиска. Это также избавляет от переписывания одного и того же выражения


⦁	Сегментация и токенезация текста на естественном языке, стеммминг и лемматизация, примеры на Python 
Сегментация — это процесс разбиения текста по любому возможному признаку.
Токенизация — это процесс разбиения текста по какому-либо конкретному признаку (например, по пробелам, точкам, запятым и т.д.)

Для токенизации используется библиотека nltk.

Стемминг — это процесс нахождения основы слова, которая не обязательно совпадает с корнем слова.
Стемминг отбрасывает суффиксы и окончания до неизменяемой формы слова
Примеры:
кошка -> кошк
кошками -> кошк

Лемматизация - приведение слова к словарной форме, например:
кошки -> кошка
кошками -> кошка

⦁	Расстояние Левеншнтейна: определение, алгоритм эффективного поиска оптимального редакционного предписания, пример поиска на Python 
Расстояние Левенштейна - минимальное количество операций необходимых для превращения одной̆ строки в другую. 

Для поиска расстояния Левинштайна существует эффективный алгоритм, требующий̆ операциии ( и это длины первой и второй строки соответственно).
Редакционным предписанием называется последовательность действий̆, необходимых для получения второй̆ строки из первой̆ кратчайшим образом. Обычно действия обозначаются так:
D (англ. delete) — удалить I (англ. insert) — вставить R (replace) — заменить
M (match) — совпадение.
По сути, редакционное предписание — это кротчайшие пути на графе с весами, в котором существует 3 вида ориентированных ребер (D, I, M), а вершинами являются строки (слова). В общем случае для конкретной̆ пары слов может существовать несколько редакционных предписаний (кратчайших путей на графе).


"""

def sem1():
    return """

Сгенерировать двухмерный массив arr размерности (4, 7), состоящий из случайных действительных чисел, равномерно распределенных в диапазоне от 0 до 20. Нормализовать значения массива с помощью преобразования вида 
 так, что после нормализации максимальный элемент масcива будет равен 1.0, минимальный 0.0
import numpy as np
import random
arr = np.random.uniform(0, 20, size=(4, 7))
ans_1=(arr-arr.min())/(arr.max()-arr.min())
print(arr)
print(ans_1)


Создать матрицу 8 на 10 из случайных целых (используя модуль numpy.random) чисел из диапозона от 0 до 10 и найти в ней строку (ее индекс и вывести саму строку), в которой сумма значений минимальна.
##task2
ans_2=np.random.randint(0,10,(8,10))
ans_2s=np.argmin(np.sum(ans_2,1))
print(ans_2)
print(ans_2s)
print(ans_2[ans_2s])

Найти евклидово расстояние между двумя одномерными векторами одинаковой размерности.
##task3
x=np.random.random((1,3))
y=np.random.random((1,3))
ans_3=np.linalg.norm(x-y)
ans_3


Решить матричное уравнение A*X*B=-C - найти матрицу X. Где A = [[-1, 2, 4], [-3, 1, 2], [-3, 0, 1]], B=[[3, -1], [2, 1]], C=[[7, 21], [11, 8], [8, 4]].
##task4
##A*X*B=-C
##x=a^(-1)*(-c)*b^(-1)
A = np.array([[-1, 2, 4], [-3, 1, 2], [-3, 0, 1]])
B=np.array([[3, -1], [2, 1]])
C=np.array([[7, 21], [11, 8], [8, 4]])
a 
c=-C
X=a@c@b
print(x)

#лабораторная работа
Файл minutes_n_ingredients.csv содержит информацию об идентификаторе рецепта, времени его выполнения в минутах и количестве необходимых ингредиентов. Считайте данные из этого файла в виде массива numpy типа int32, используя np.loadtxt. Выведите на экран первые 5 строк массива.
##task1
import numpy as np

with open("minutes_n_ingredients.csv") as file_name:
    arr = np.loadtxt(file_name, delimiter=",",skiprows=1,dtype=np.int32)

print(arr[:5])

Вычислите среднее значение, минимум, максимум и медиану по каждому из столбцов, кроме первого.
##task2
mean = np.mean(arr[:,1:], axis=0)
minn = np.min(arr[:, 1:], axis=0)
maxx = np.max(arr[:, 1:], axis=0)
med = np.median(arr[:, 1:], axis=0)

print('Среднее',mean)
print('Минимум:', minn)
print('Максимум:', maxx)
print('Медиана:', med)

Ограничьте сверху значения продолжительности выполнения рецепта значением квантиля 
## task3
q = np.quantile(arr, 0.75)
print("Значение квантиля уровня 0.75:", q)

arr_3 = np.where(arr[:,1] > q, q, arr[:,1])
print(arr_3)

Посчитайте, для скольких рецептов указана продолжительность, равная нулю. Замените для таких строк значение в данном столбце на 1.
##task4
print(np.count_nonzero (arr[:,1] == 0 ))
zero_duration_indices = np.where(arr[:, 1] == 0)[0]

arr[zero_duration_indices, 1] = 1

print("Обновленные значения продолжительности выполнения рецепта:")
print(arr [411])

Посчитайте, сколько уникальных рецептов находится в датасете.
##task5
arr5=np.unique(arr[:,0], axis=0)
print(len(arr5))

Сколько и каких различных значений кол-ва ингредиентов присутвует в рецептах из датасета?
## task 6
arr6=np.unique(arr[:,2])
print(arr6)
print(len(arr6))

Создайте версию массива, содержащую информацию только о рецептах, состоящих не более чем из 5 ингредиентов.
## task7
arr7=arr.copy()
ans7=np.array(arr7[:,2])
ans7=ans7-5<=0
print(arr7[ans7])

Для каждого рецепта посчитайте, сколько в среднем ингредиентов приходится на одну минуту рецепта. Найдите максимальное значение этой величины для всего датасета
## task8
arr8_1=np.array((arr[:,2]/arr[:,1]))
max8=np.max(arr8_1)
print(max8)
print(arr8_1)

Вычислите среднее количество ингредиентов для топ-100 рецептов с наибольшей продолжительностью
##task 9
arr9=arr.copy()
arr9=arr9[arr9[:,2].argsort()[::-1]]
print(arr9)
ans9 = np.mean(arr9[0:99,:], axis=0)
ans9[2]

Выберите случайным образом и выведите информацию о 10 различных рецептах
## task10
for _ in range (10):
    ans10=np.random.randint(0,len(arr))
    print(arr[ans10])

Выведите процент рецептов, кол-во ингредиентов в которых меньше среднего.    
## task 11
arr11=arr.copy()
ans11=np.array(arr11[:,2])
cos = np.mean(arr11[:,:], axis=0)
ans11=ans11-cos[2]<=0
print(len(arr11[ans11])/len(arr)*100,'%')

Назовем "простым" такой рецепт, длительность выполнения которого не больше 20 минут и кол-во ингредиентов в котором не больше 5. Создайте версию датасета с дополнительным столбцом, значениями которого являются 1, если рецепт простой, и 0 в противном случае.
## task 12
arr_12=[(1 if arr[i,1] <=20 and arr[i,2]<=5 else 0) for i in range(len(arr))]
arr12=np.array(arr_12).reshape((len(arr_12),1))
ans12=np.hstack((arr,arr12))
print(ans12)

Выведите процент "простых" рецептов в датасете
## task 13
ans13=np.count_nonzero(arr12==1)
print(ans13/len(arr)*100,'%')

Разделим рецепты на группы по следующему правилу. Назовем рецепты короткими, если их продолжительность составляет менее 10 минут; стандартными, если их продолжительность составляет более 10, но менее 20 минут; и длинными, если их продолжительность составляет не менее 20 минут. Создайте трехмерный массив, где нулевая ось отвечает за номер группы (короткий, стандартный или длинный рецепт), первая ось - за сам рецепт и вторая ось - за характеристики рецепта. Выберите максимальное количество рецептов из каждой группы таким образом, чтобы было возможно сформировать трехмерный массив. Выведите форму полученного массива.
## task 14
short = arr[arr[:, 1] <= 10]
norm = arr[(arr[:, 1] > 10) & (arr[:, 1] < 20)]
longg = arr[arr[:, 1] >= 20]

maxx = min([short.shape[0], norm.shape[0], longg.shape[0]])
print(np.array([short[:maxx], norm[:maxx], longg[:maxx]]))


##1
ans1=np.arange(3,10)
print('1) ',ans1)



##2
ans2=np.zeros(15)
ans2[4]=1
print('2) ', ans2)


##3

ans3=np.array([[i,i+1,i+2] for i in range(0,9,3)])
print('3) ', ans3)


##4

a=np.array([1,2,0,0,4,0])
ans4=len([i for i in a if i>0])
print('4) ',ans4)



##5

a1=np.array([[i,i+1,i+2] for i in range(0,15,3)])
a2=np.array([[i,i+1] for i in range(0,6,2)])
ans5=np.dot(a1,a2)
print('5) ',ans5)



##6
ans6=np.ones((10,10))
ans6[0, :] = 0
ans6[-1, :] = 0
ans6[:, 0] = 0
ans6[:, -1] = 0
print('6) ',ans6)

##7
ans7=np.random.rand(10)
ans7_s=np.sort(ans7)
print(ans7)
print(ans7_s)


##8
arr8=np.array([[1, 2, 3],[4, 5, 6]])
for i,j in np.ndenumerate(arr8):
    print(i,j)
    
    
##9
arr9=np.random.rand(5,1)
m=np.mean(arr9,axis=0)
s=np.std(arr9,axis=0)
n_arr9=(arr9-m)/s
print(arr9)
print(n_arr9)


##10
numm=int(input('Введите число '))
vekk=np.array([1,2,3,4,5,6,7,8,9])
ans10=vekk[(np.abs(vekk-numm)).argmin()]
print(ans10)

##11
vek=np.array([1,2,7,6,113,4,5,6,7,8,9])
n=int(input('Введите количество '))
ans12=np.sort(vek)[-n:][::-1]
print(ans12)



"""

def sem2():
    return """
Загрузите данные из файла sp500hst.txt и обозначьте столбцы в соответствии с содержимым: "date", "ticker", "open", "high", "low", "close", "volume".

import numpy as np 
import pandas as pd
import csv

##task1
tab_1=pd.read_csv('sp500hst.txt',sep=",", names=["date", "ticker", "open", "high", "low", "close", "volume"])
print(tab_1)

Рассчитайте среднее значение показателей для каждого из столбцов c номерами 3-6.
##task2
tab_11 = pd.DataFrame(tab_1)
mean = tab_11.iloc[:, 2:7].mean()
mean

Добавьте столбец, содержащий только число месяца, к которому относится дата.
## task 3
tab_1['month']=tab_1['date'].apply(lambda y: str(y)[4:6])
print(tab_1)

Рассчитайте суммарный объем торгов для для одинаковых значений тикеров.
## task 4
total_volume = tab_1.groupby('ticker')['volume'].sum()
print(total_volume)

Загрузите данные из файла sp500hst.txt и обозначьте столбцы в соответствии с содержимым: "date", "ticker", "open", "high", "low", "close", "volume". Добавьте столбец с расшифровкой названия тикера, используя данные из файла sp_data2.csv . В случае нехватки данных об именах тикеров корректно обработать их.
## task 5
tab_2=pd.read_csv('sp_data2.csv',sep=";", names=["ticker", "company", "percentage"])
tab_cos=tab_2.iloc[:, 0:2]
tab_5=pd.merge(tab_1,tab_cos)
tab_5.fillna('Неизвестно')
print(tab_5)

В файлах recipes_sample.csv и reviews_sample.csv находится информация об рецептах блюд и отзывах на эти рецепты соответственно. Загрузите данные из файлов в виде pd.DataFrame с названиями recipes и reviews. Обратите внимание на корректное считывание столбца с индексами в таблице reviews (безымянный столбец).
## лабораторная 2
## task 1
tab_3=pd.read_csv('recipes_sample.csv',parse_dates=['submitted'])
tab_4=pd.read_csv('reviews_sample.csv',index_col=0)
print(tab_3)
print(tab_4)

Для каждой из таблиц выведите основные параметры:

количество точек данных (строк);
количество столбцов;
тип данных каждого столбца.
## task 2
print('таблица 1')
print("Количество точек данных (строк):", tab_3.shape[0])
print("Количество столбцов:", tab_3.shape[1])
print("Тип данных каждого столбца:")
print(tab_3.dtypes)

print('таблица 2')
print("Количество точек данных (строк):", tab_4.shape[0])
print("Количество столбцов:", tab_4.shape[1])
print("Тип данных каждого столбца:")
print(tab_4.dtypes)

Исследуйте, в каких столбцах таблиц содержатся пропуски. Посчитайте долю строк, содержащих пропуски, в отношении к общему количеству строк.
recipes=tab_3.copy()
reviews=tab_4.copy()
## task 3
rows_with_missing_recipies = tab_3[tab_3.isna().any(axis=1)]
rows_with_missing_reviews = tab_4[tab_4.isna().any(axis=1)]

print('Количество пропусков в столбцах recipes:' , tab_3.isna().sum() , sep = '\n')
print('Количество пропусков в столбцах reviews:' , tab_4.isna().sum() , sep = '\n')
print(f'Доля строк, содержащих пропуски, в отношении к общему количеству строк recipes: {len(rows_with_missing_recipies) / len(tab_3)}')
print(f'Доля строк, содержащих пропуски, в отношении к общему количеству строк reviews: {len(rows_with_missing_reviews) / len(tab_4)}')


Рассчитайте среднее значение для каждого из числовых столбцов (где это имеет смысл).
## task 4
num_rows_recipes = tab_3.select_dtypes(include=['float64', 'int64']).mean()
num_rows_reviews = tab_4.select_dtypes(include=['float64', 'int64']).mean()
print(f'Средние значения для числовых столбцов в таблице recipes:', num_rows_recipes, sep = '\n')
print(f'Средние значения для числовых столбцов в таблице reviews:', num_rows_reviews, sep = '\n')

Создайте серию из 10 случайных названий рецептов.
## task 5
print( 'Cерия из 10 случайных названий рецептов', tab_3['name'].sample(n=10), sep = '\n')

Измените индекс в таблице reviews, пронумеровав строки, начиная с нуля.
## task 6
print(tab_4)
print(tab_4.reset_index(drop=True))
tab_4 = tab_4.reset_index(drop=True)

Выведите информацию о рецептах, время выполнения которых не больше 20 минут и кол-во ингредиентов в которых не больше 5.
## task 7
filtered_recipies = recipes[(recipes['minutes'] <= 20) & (recipes['n_ingredients'] <= 5)]
print(filtered_recipies)

Преобразуйте столбец submitted из таблицы recipes в формат времени. Модифицируйте решение задачи 1.1 так, чтобы считать столбец сразу в нужном формате.
## task 2.1
print(recipes.head())
print(recipes['submitted'])

Выведите информацию о рецептах, добавленных в датасет не позже 2010 года.
## task 2.2
recipes_early_2010 = recipes[recipes['submitted'].dt.year <= 2010]
print(recipes_2010['submitted'].sample(n=10))

Добавьте в таблицу recipes столбец description_length, в котором хранится длина описания рецепта из столбца description.
## task 3.1
recipes['description_length'] = recipes['description'].apply(lambda x: len(str(x)))
print(recipes)

Измените название каждого рецепта в таблице recipes таким образом, чтобы каждое слово в названии начиналось с прописной буквы.
## task 3.2
recipes['name'] = recipes['name'].apply(lambda x: x.title())
print(recipes)

Добавьте в таблицу recipes столбец name_word_count, в котором хранится количество слов из названии рецепта (считайте, что слова в названии разделяются только пробелами). Обратите внимание, что между словами может располагаться несколько пробелов подряд.
## task 3.3
recipes['name_word_count'] = recipes['name'].apply(lambda x: len(str(x).split()))
print(recipes)

Посчитайте количество рецептов, представленных каждым из участников (contributor_id). Какой участник добавил максимальное кол-во рецептов?
## task 4.1
recipes_counts = recipes['contributor_id'].value_counts()
maxx = recipes_counts.idxmax()
print('Максимальное количество рецептов:',recipes_counts.max())
print('contributor_id :',maxx)

Посчитайте средний рейтинг к каждому из рецептов. Для скольких рецептов отсутствуют отзывы? Обратите внимание, что отзыв с нулевым рейтингом или не заполненным текстовым описанием не считается отсутствующим.
## task 4.2
reviews_raiting = reviews.groupby('recipe_id')['rating'].mean()
print(reviews_raiting.head())

cos = len(set(recipes['id']) - set(reviews['recipe_id']))
print(cos)

Посчитайте количество рецептов с разбивкой по годам создания.
##task 4.3
coss = recipes['submitted'].dt.year.value_counts()
print('Количество рецептов с разбивкой по годам создания:', coss, sep = '\n')


При помощи объединения таблиц, создайте DataFrame, состоящий из четырех столбцов: id, name, user_id, rating. Рецепты, на которые не оставлен ни один отзыв, должны отсутствовать в полученной таблице. Подтвердите правильность работы вашего кода, выбрав рецепт, не имеющий отзывов, и попытавшись найти строку, соответствующую этому рецепту, в полученном DataFrame.
## task 5.1
reviews_with_drop=reviews.dropna(axis=0)
print(reviews_with_drop.shape)
recipes_reviwes = pd.merge(recipes[['id', 'name']], reviews_with_drop[['user_id', 'recipe_id', 'rating']], left_on='id', right_on='recipe_id')
print(recipes_reviwes)
recipes_reviwes.iloc()[19]

При помощи объединения таблиц и группировок, создайте DataFrame, состоящий из трех столбцов: recipe_id, name, review_count, где столбец review_count содержит кол-во отзывов, оставленных на рецепт recipe_id. У рецептов, на которые не оставлен ни один отзыв, в столбце review_count должен быть указан 0. Подтвердите правильность работы вашего кода, выбрав рецепт, не имеющий отзывов, и найдя строку, соответствующую этому рецепту, в полученном DataFrame.
## task 5.2
review_count = reviews.groupby('recipe_id').size().reset_index(name='review_count')
print(review_count)
for i in range(28100):
    if review_count['review_count'][i]==0:
        print(i)
merged = pd.merge(recipes[['id', 'name']], reviews[['recipe_id']], left_on='id', right_on='recipe_id', how='left')
grouped = merged.groupby(['recipe_id', 'name']).size().reset_index(name='review_count')
grouped['review_count'] = grouped['review_count'].fillna(0).astype(int)
grouped['recipe_id'] = grouped['recipe_id'].astype(int)
print(grouped)

Выясните, рецепты, добавленные в каком году, имеют наименьший средний рейтинг?
## task 5.3
rating = pd.merge(recipes[['id', 'submitted']], reviews[['recipe_id', 'rating']], left_on='id', right_on='recipe_id')
rating['year_submitted'] = rating['submitted'].dt.year
result = rating.groupby('year_submitted')['rating'].mean()
print(result.idxmin(), result.min())

Отсортируйте таблицу в порядке убывания величины столбца name_word_count и сохраните результаты выполнения заданий 3.1-3.3 в csv файл.
## task 6.1
recipes['description_length'] = recipes['description'].apply(lambda x: len(str(x)))
recipes['name'] = recipes['name'].apply(lambda x: x.title())
recipes['name_word_count'] = recipes['name'].apply(lambda x: len(str(x).split()))
recipes = recipes.sort_values(by='name_word_count', ascending=False)
print(recipes)
recipes.to_csv(r'aa.csv')

Воспользовавшись pd.ExcelWriter, cохраните результаты 5.1 и 5.2 в файл: на лист с названием Рецепты с оценками сохраните результаты выполнения 5.1; на лист с названием Количество отзывов по рецептам сохраните результаты выполнения 5.2.
## task 6.2
# Создание датафрейма для результата выполнения задания 5.1
data_5_1 = recipes_reviwes
df_5_1 = pd.DataFrame(data_5_1)

# Создание датафрейма для результата выполнения задания 5.2
data_5_2 = grouped
df_5_2 = pd.DataFrame(data_5_2)

# Запись результатов в файл
with pd.ExcelWriter('bb.xlsx') as writer:
    df_5_1.to_excel(writer, sheet_name='Рецепты с оценками', index=False)
    df_5_2.to_excel(writer, sheet_name='Количество отзывов по рецептам', index=False)
"""



def sem3():
    return """

Вывести все адреса электронной почты, содержащиеся в адресной книге addres-book.json
import numpy as np 
import pandas as pd
import csv

import json
with open("addres-book.json") as f:
    tab_1=json.load(f)
for i in tab_1:
    print(i['email'])
    
Вывести телефоны, содержащиеся в адресной книге addres-book.json
for i in tab_1:
    for j in i['phones']:
        print(j['phone'])
        
По данным из файла addres-book-q.xml сформировать список словарей с телефонами каждого из людей.
import requests
from bs4 import BeautifulSoup
with open('addres-book-q.xml') as f: 
    ab = BeautifulSoup(f, 'xml')

res=list()
for person in ab.address_book.find_all("address"):
    ph = [phones.next for phones in person.phones.find_all("phone")]
    res.append({person.find("name").next: ph})
res

JSON
1.1 Считайте файл contributors_sample.json. Воспользовавшись модулем json, преобразуйте содержимое файла в соответствующие объекты python. Выведите на экран информацию о первых 3 пользователях.

with open("contributors_sample.json") as f:
    tab_2=json.load(f)
print(tab_2[:3])

1.2 Выведите уникальные почтовые домены, содержащиеся в почтовых адресах людей

anser=list()
for i in tab_2:
    cos=i['mail'][i['mail'].find('@')+1:]
    if cos not in anser:
        anser.append(cos)
print(anser)

1.3 Напишите функцию, которая по username ищет человека и выводит информацию о нем. Если пользователь с заданным username отсутствует, возбудите исключение ValueError

def findd(a,usern):
    for i in a:
        if i['username'] == usern:
            return i
    raise ValueError("Пользователь с username '" + usern + "'не найден")
print(findd(tab_2,'nicole82'))
print(findd(tab_2,'n'))

1.4 Посчитайте, сколько мужчин и женщин присутсвует в этом наборе данных.

men=0
women=0
for i in tab_2:
    if i['sex']=='F':
        women+=1
    if i['sex']=='M':
        men+=1
print(men,women)

1.5 Создайте pd.DataFrame contributors, имеющий столбцы id, username и sex.

coss = pd.DataFrame(tab_2)
contributors=coss[['id','username','sex']]
print(contributors)


1.6 Загрузите данные из файла recipes_sample.csv (ЛР2) в таблицу recipes. Объедините recipes с таблицей contributors с сохранением строк в том случае, если информация о человеке отсутствует в JSON-файле. Для скольких человек информация отсутствует?



import csv
recipes=pd.read_csv('recipes_sample.csv',sep=",")
tab_16=pd.merge(recipes, contributors)
missing_contributors = len((tab_16['username']))
print(len(recipes)-missing_contributors)


pickle
2.1 На основе файла contributors_sample.json создайте словарь следующего вида:

{
    должность: [список username людей, занимавших эту должность]
}

with open("contributors_sample.json") as f:
    tab_3=json.load(f)
ans21={}
for i in tab_3:
    for j in i['jobs']:
        ans21[j]=list()
for i in tab_3:
    for j in i['jobs']:
        ans21[j].append(i['username'])
    
print(ans21)


Сохраните результаты в файл job_people.pickle и в файл job_people.json с использованием форматов pickle и JSON соответственно. Сравните объемы получившихся файлов. При сохранении в JSON укажите аргумент indent.

import pickle


with open('job_people.pickle', 'wb') as f:
    pickle.dump(ans21, f)

with open("job_people.pickle", mode="w") as f:
    json.dump(ans21, f, indent=3)
import os

pickle_size = os.path.getsize('job_people.pickle')
json_size = os.path.getsize('job_people.json')
print(pickle_size)
print(json_size)

2.3 Считайте файл job_people.pickle и продемонстрируйте, что данные считались корректно.
with open("job_people.pickle", "rb") as file:
    loaded_data = pickle.load(file)
print(loaded_data == positions_dict)

XML
3.1 По данным файла steps_sample.xml сформируйте словарь с шагами по каждому рецепту вида {id_рецепта: ["шаг1", "шаг2"]}. Сохраните этот словарь в файл steps_sample.json
with open('steps_sample.xml') as f: 
    tab_31 = BeautifulSoup(f, 'xml')

res=list()
for recip in tab_31.recipes.find_all("recipe"):
    steps = [step.next for step in recip.steps.find_all("step")]
    res.append({recip.find("id").next:steps})
res

with open('steps_sample.json', 'w') as f:
    json.dump(res, f, indent=4)3.2 
    
По данным файла steps_sample.xml сформируйте словарь следующего вида: кол-во_шагов_в_рецепте: [список_id_рецептов]
ans_32={}
for i in res:
    ans_32[len(i.values())]=list()
for i in res:
    ans_32[len(i.values())].append(int(list(i.keys())[0]))
ans_32

3.3 Получите список рецептов, в этапах выполнения которых есть информация о времени (часы или минуты). Для отбора подходящих рецептов обратите внимание на атрибуты соответствующих тэгов.
recipe_with_time = []
for recipe in tab_31.recipes.find_all('recipe'):
    id = int(recipe.find("id").next)
    for steps in recipe.steps.find_all('step'):
        if steps.get('has_minutes') or steps.get('has_hours'):
            recipe_with_time.append(id)
            break

recipe_with_time

3.4 Загрузите данные из файла recipes_sample.csv (ЛР2) в таблицу recipes. Для строк, которые содержат пропуски в столбце n_steps, заполните этот столбец на основе файла steps_sample.xml. Строки, в которых столбец n_steps заполнен, оставьте без изменений.
for n_step, ids in ans_32.items():
    for id in ids:
        ind = recipes[recipes['id'] == id].index[0]
        recipes.at[ind, 'n_steps'] = n_step

print(recipes.info(), recipes.head())

3.5 Проверьте, содержит ли столбец n_steps пропуски. Если нет, то преобразуйте его к целочисленному типу и сохраните результаты в файл recipes_sample_with_filled_nsteps.csv
recipes['n_steps'] = recipes['n_steps'].astype(int)
recipes.to_csv('recipes_sample_with_filled_nsteps.csv')

        
"""

def sem4():
    return """
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import seaborn as sns
import matplotlib.ticker as ticker
import seaborn as sns


##pip install pandas_datareader

1. В файле average_ratings.npy содержится информация о среднем рейтинге 3 рецептов за период с 01.01.2019 по 30.12.2021. При помощи пакета matplotlib в одной системе координат (на одной картинке) изобразите три временных ряда, соответствующих средним рейтингам этих рецептов.

По горизонтальной оси располагается номер дня (0, 1, 2, ...), по вертикальной - средний рейтинг рецептов в этот день.

Названия рецептов и их индексы в файле average_ratings.npy:

0: waffle iron french toast
1: zwetschgenkuchen bavarian plum cake
2: lime tea
Результатом работы является визуализация, на которой:

добавлена подпись горизонтальной оси с текстом "Номер дня"
добавлена подпись вертикальной оси с текстом "Средний рейтинг"
добавлена подпись рисунка с текстом "Изменение среднего рейтинга трех рецептов"
каждый из временных рядов имеет уникальный цвет
добавлена легенда, на которой отображается название каждого из рецептов
Примечание : для считывания файла воспользуйтесь функцией np.load.

ratings = np.load("data/average_ratings.npy")


days = np.arange(ratings.shape[1])
recipe_names = ['waffle iron french toast', 'zwetschgenkuchen bavarian plum cake', 'lime tea']


plt.figure(figsize=(10, 6))
plt.plot(days, ratings[0], label=recipe_names[0], color='green')
plt.plot(days, ratings[1], label=recipe_names[1], color='blue')
plt.plot(days, ratings[2], label=recipe_names[2], color='r')


plt.xlabel('Номер дня')
plt.ylabel('Средний рейтинг')
plt.title('Изменение среднего рейтинга трех рецептов')

plt.legend()

plt.show()


2. Измените визуализацию, полученную в задании 1, таким образом, чтобы по горизонтальной оси отображались года, а между двумя соседними годами располагались засечки, соответствующие месяцам. Для этого создайте диапазон дат от 01.01.2019 по 30.12.2021 с шагом в один день (например, вот так) и используйте этот диапазон при вызове метода plot. Далее настройте major_locator и minor_locator горизонтальной оси (подробнее см. здесь)

Примените к получившемуся рисунку цвета графиков, подписи, легенду из задания 1. Измените подпись горизонтальной оси, написав там слово "Дата".

data = pd.date_range(start='2019-01-01', end='2021-12-30')
ratings = np.load("data/average_ratings.npy")

plt.figure(figsize=(10, 6))
plt.plot(data, ratings[0], label=recipe_names[0], color='green')
plt.plot(data, ratings[1], label=recipe_names[1], color='blue')
plt.plot(data, ratings[2], label=recipe_names[2], color='red')


plt.gca().xaxis.set_major_locator(mdates.YearLocator())
plt.gca().xaxis.set_minor_locator(mdates.MonthLocator())
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y'))

plt.xlabel('Дата')
plt.ylabel('Средний рейтинг')
plt.title('Изменение среднего рейтинга трех рецептов')


plt.legend()

plt.show()

3. Измените визуализацию, полученную в задании 2, разбив одну картинку на три, расположенных друг под другом. Три изображения должны иметь одну общую горизонтальную ось (каждое изображение засечки в нижней части, но значения этих засечек находятся только под самым нижним изображением). Примените к получившемуся рисунку цвета графиков, подписи, легенду из задания 2.



fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(10, 6), sharex=True)


ax1.plot(data, ratings[0], label=recipe_names[0], color='green')
ax1.set_ylabel('Средний рейтинг')


ax2.plot(data, ratings[1], label=recipe_names[1], color='blue')
ax2.set_ylabel('Средний рейтинг')


ax3.plot(data, ratings[2], label=recipe_names[2], color='red')
ax3.set_ylabel('Средний рейтинг')



ax3.xaxis.set_major_locator(mdates.YearLocator())
ax3.xaxis.set_minor_locator(mdates.MonthLocator())
ax3.xaxis.set_major_formatter(mdates.DateFormatter('%Y'))

ax1.legend()
ax2.legend()
ax3.legend()


plt.show()

В файле visitors.npy представлена информация о количестве посетителей сайта в течении первых 100 дней после объявления сайтом акции. Постройте график изменения количества пользователей в зависимости от дня в двух вариантах, расположенных рядом по горизонтале. В обоих случаях изобразите график в виде ломаной, но в первом случае оставьте линейный масштаб осей, а во втором случае сделайте вертикальную ось в логарифмическом масштабе. Добавьте на обе картинки подпись над этим графиком к текстом 

Добавьте на оба изображения красную горизонтальную линию на уровне 
. Добавьте на обе картинки подпись над этой линией с текстом 

Добавьте на оба изображения подписи осей; горизонтальную ось подпишите текстом "Количество дней с момента акции", вертикальную - "Число посетителей".

Добавьте общий заголовок для фигуры с текстом "Изменение количества пользователей в линейном и логарифмическом масштабе".

visitors = np.load('data/visitors.npy')

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 6))


ax1.plot(visitors, label='y(x) = λe^(-λx)')
ax1.set_xlabel('Количество дней с момента акции')
ax1.set_ylabel('Число посетителей')
ax1.set_title('y(x) = λe^(-λx)')


ax2.plot(visitors, label='y(x) = λe^(-λx)')
ax2.set_xlabel('Количество дней с момента акции')
ax2.set_ylabel('Число посетителей')
ax2.set_title('y(x) = λe^(-λx)')
ax2.set_yscale('log')


ax1.axhline(100, color='red', linestyle='--', label='y(x) = 100')
ax2.axhline(100, color='red', linestyle='--', label='y(x) = 100')
ax2.text(days[-1], 100, 'y(x) = 100', ha='right')
ax1.text(days[-1], 100, 'y(x) = 100', ha='right')
ax1.legend()
ax2.legend()

fig.suptitle('Изменение количества пользователей в линейном и логарифмическом масштабе')

plt.show()

Визуализация данных на основе структур pandas.
Для продолжения работы загрузите таблицы recipes и reviews (ЛР2)

recipes = pd.read_csv('data/recipes_sample.csv', dtype={'minutes': int})
reviews = pd.read_csv('data/reviews_sample.csv',parse_dates=['date'])

Назовем рецепты короткими, если они занимают строго меньше 5 минут; средними, если они занимают от 5 до 50 минут (не включая 50), и длинными, если они занимают от 50 минут и больше. Сгруппируйте все рецепты по данному признаку и для каждой группы посчитайте 2 величины: среднее количество шагов рецептов в группе и размер группы. При помощи методов структур pandas постройте столбчатую диаграмму, где каждый столбец означает группу (короткие, средние или длинные рецепты), а высота столбца обозначает среднее количество шагов рецептов в группе. Рядом по горизонтали разместите круговую диаграмму, на которой отображены размеры каждой из групп.

Добавьте следующие подписи:

по горизонтальной оси под столбчатой диаграммой напишите "Группа рецептов"
по вертикальной оси слева от столбчатой диаграммы напишите "Средняя длительность"
над круговой диаграммой напишите "Размеры групп рецептов"

data = pd.DataFrame({
    'group': ['Короткие', 'Средние', 'Длинные'],
    'mean_steps': [short_mean, medium_mean, long_mean],
    'size': [len(short), len(medium), len(long)]
})

print(data)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 6))

ax1.bar(data['group'], data['mean_steps'])
ax1.set_xlabel('Группа рецептов')
ax1.set_ylabel('Средняя длительность')
ax1.set_title('Средняя длительность рецептов в разных группах')

ax2.pie(data['size'], labels=data['group'], autopct='%1.1f%%')
ax2.set_title('Размеры групп рецептов')

fig.suptitle('Анализ рецептов по длительности')

plt.show()

 Из всего множества отзывов оставьте только те, которые были даны в 2008 и 2009 годах. Воспользовавшись возможностями метода pd.DataFrame.plot.hist, постройте 2 гистограммы столбца rating. Гистограммы должны быть расположены рядом по горизонтали. Левая гистограмма соотвествует 2008 году, правая - 2009 году. Добавьте общую подпись для рисунка с текстом "Гистограммы рейтинга отзывов в 2008 и 2009 годах". Добейтесь того, чтобы подпись вертикальной оси правого рисунка не "наезжала" на левый рисунок.
 
 reviews = reviews[(reviews['date'].dt.year == 2008) | (reviews['date'].dt.year == 2009)]

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 6))

ax1.hist(reviews[reviews['date'].dt.year == 2008]['rating'])
ax1.set_title('2008')

ax2.hist(reviews[reviews['date'].dt.year == 2009]['rating'])
ax2.set_title('2009')

fig.suptitle('Гистограммы рейтинга отзывов в 2008 и 2009 годах')

plt.show()

Визуализация данных при помощи пакета seaborn
7. При помощи пакета seaborn постройте диаграмму рассеяния двух столбцов из таблицы recipes: n_steps и n_ingredients. Укажите в качестве группирующей переменной (hue) категориальную длительность рецепта (короткий, средний или длинные; см. задание 5).

Добавьте заголовок рисунка "Диаграмма рассеяния n_steps и n_ingredients"

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

merged_data = pd.merge(recipes, reviews, left_on='id', right_on='recipe_id')
merged_data['duration'] = pd.cut(merged_data['minutes'], bins=[0, 5, 50, np.inf], labels=['короткие', 'средние', 'длинные'])

sns.scatterplot(data=merged_data, x='n_steps', y='n_ingredients', hue='duration')
plt.title('Диаграмма рассеяния n_steps и n_ingredients')
plt.show()

8. Объедините две таблицы recipes и reviews и постройте корреляционную матрицу на основе столбцов "minutes", "n_steps", "n_ingredients" и "rating". При помощи пакета seaborn визуализируйте полученную матрицу в виде тепловой карты (heatmap).

Добавьте в ячейки тепловой карты подписи (значения к-та корреляции). Измените цветовую палитру на YlOrRd.

Добавьте заголовок рисунка "Корреляционная матрица числовых столбцов таблиц recipes и reviews"

cor_data = merged_data[['minutes', 'n_steps', 'n_ingredients', 'rating']].corr()
sns.heatmap(cor_data,annot=True,  cmap='YlOrRd')
plt.title('Корреляционная матрица числовых столбцов таблиц recipes и reviews')
plt.show()




"""

