Загрузка данных
# # Лабораторная работа №1
# ## Знакомство с библиотекой Pandas. Объекты Series и DataFrame
# ### Задание 1. Импорт библиотек и настройка вывода
# Комментарий: Импортируем необходимые библиотеки и настраиваем параметры отображения Pandas
# для удобной работы в Jupyter Notebook/Google Colab.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# Настройка отображения Pandas - ограничиваем количество выводимых строк и столбцов
pd.set_option("display.max_columns", 10)
pd.set_option("display.max_rows", 10)
pd.set_option("display.width", 80)
# Для отображения графиков прямо в ноутбуке (в Colab/Jupyter)
%matplotlib inline
print("=" * 60)
print("ЗАДАНИЕ 1: Библиотеки импортированы и настроены")
print("=" * 60)
# ### Задание 2. Создание объектов Series
# Комментарий: Series можно создавать разными способами: из списка, словаря, массива NumPy
# и даже из скалярного значения с указанием количества повторений.
# 1. Из списка Python - самый простой способ, индекс создаётся автоматически (0,1,2...)
s1 = pd.Series([10, 20, 30, 40, 50])
print("\n1. Series из списка Python (автоиндекс):")
print(s1)
print("Комментарий: Индекс создан автоматически от 0 до 4")
# 2. Из словаря - ключи становятся индексами, значения - данными
s2 = pd.Series({"a": 1, "b": 2, "c": 3, "d": 4})
print("\n2. Series из словаря (ключи как индексы):")
print(s2)
print("Комментарий: Буквы 'a','b','c','d' стали индексами")
# 3. С помощью np.arange и указанием индекса - явно задаём и значения, и индексы
s3 = pd.Series(np.arange(5, 10), index=['v', 'w', 'x', 'y', 'z'])
print("\n3. Series из np.arange с пользовательским индексом:")
print(s3)
print("Комментарий: Значения 5-9 привязаны к индексам v-z")
# 4. Из скалярного значения - одно значение повторяется указанное количество раз
s4 = pd.Series(5, index=range(4))
print("\n4. Series из скалярного значения 5 (повтор 4 раза):")
print(s4)
print("Комментарий: Значение 5 повторяется для каждого индекса")
print("\n" + "=" * 60)
print("ЗАДАНИЕ 2 ВЫПОЛНЕНО: Созданы 4 различных Series")
print("=" * 60)
# ### Задание 3. Свойства index и values
# Комментарий: .index возвращает метки (индексы), .values возвращает массив значений.
# Это ключевые атрибуты для доступа к компонентам Series.
print("\nЗАДАНИЕ 3: Свойства index и values")
print("Для Series s3 (из задания 2):")
print(f" .index: {s3.index}")
print(f" .values: {s3.values}")
print(f" Тип index: {type(s3.index)}")
print(f" Тип values: {type(s3.values)}")
print("Комментарий: index - специальный объект Index, values - массив NumPy")
print("\n" + "=" * 60)
print("ЗАДАНИЕ 3 ВЫПОЛНЕНО")
print("=" * 60)
# ### Задание 4. Создание Series с помощью pd.Series()
# Комментарий: Индексом может быть что угодно, включая даты (DatetimeIndex).
# Это удобно для временных рядов.
dates = pd.date_range('20230101', periods=6)
s5 = pd.Series(np.random.randn(6), index=dates)
print("\nЗАДАНИЕ 4: Series с датами в индексе")
print(s5)
print("Комментарий: Индекс из дат (DatetimeIndex) - основа для временных рядов")
print("\n" + "=" * 60)
print("ЗАДАНИЕ 4 ВЫПОЛНЕНО")
print("=" * 60)
# ### Задание 5. Методы .head(), .tail() и .take()
# Комментарий: .head() и .tail() удобны для быстрого просмотра данных.
# .take() позволяет выбрать элементы по произвольным позициям.
s_random = pd.Series(np.random.randint(0, 100, 20))
print("\nЗАДАНИЕ 5: Методы head(), tail(), take()")
print("Исходный Series из 20 случайных чисел (0-100):")
print(s_random)
print("\n.head() - первые 5 элементов (по умолчанию):")
print(s_random.head())
print("Комментарий: .head(5) эквивалентно .head()")
print("\n.tail(7) - последние 7 элементов:")
print(s_random.tail(7))
print("Комментарий: .tail(7) показывает 7 элементов с конца")
print("\n.take([-4, -3, -2, -1]) - последние 4 элемента:")
print(s_random.take([-4, -3, -2, -1]))
print("Комментарий: Отрицательные индексы отсчитываются с конца")
print("\n" + "=" * 60)
print("ЗАДАНИЕ 5 ВЫПОЛНЕНО")
print("=" * 60)
# ### Задание 6. Доступ к элементам по метке и позиции
# Комментарий: .loc[] - доступ по меткам (включает правую границу),
# .iloc[] - доступ по позициям (как в обычных списках, правая граница не включается).
s6 = pd.Series([10, 20, 30, 40, 50], index=['a', 'b', 'c', 'd', 'e'])
print("\nЗАДАНИЕ 6: Доступ по метке и позиции")
print("Исходный Series s6:")
print(s6)
print("\nДоступ по метке 'c' через []:", s6['c'])
print("Доступ по метке 'c' через .loc[]:", s6.loc['c'])
print("Комментарий: [] и .loc[] для одной метки работают одинаково")
print("Доступ по позиции 2 через .iloc[]:", s6.iloc[2])
print("Комментарий: .iloc[2] - третий элемент (счёт с 0)")
print("\n.loc['b':'d'] - срез по меткам (ВКЛЮЧАЕТ 'd'):")
print(s6.loc['b':'d'])
print("Комментарий: В Pandas .loc ВКЛЮЧАЕТ правую границу!")
print("\n.iloc[1:3] - срез по позициям (НЕ включает позицию 3):")
print(s6.iloc[1:3])
print("Комментарий: .iloc работает как обычные списки Python")
print("\n" + "=" * 60)
print("ЗАДАНИЕ 6 ВЫПОЛНЕНО")
print("=" * 60)
# ### Задание 7. Арифметические операции и выравнивание
# Комментарий: Ключевая особенность Pandas - автоматическое выравнивание по индексам.
# Несовпадающие индексы дают NaN. Метод add с fill_value решает эту проблему.
s1 = pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])
s2 = pd.Series([10, 20, 30, 40], index=['b', 'c', 'd', 'e'])
print("\nЗАДАНИЕ 7: Арифметические операции и выравнивание")
print("s1:")
print(s1)
print("\ns2:")
print(s2)
print("\nСумма s1 + s2 (выравнивание по индексам):")
print(s1 + s2)
print("ОБЪЯСНЕНИЕ: NaN появляется для индексов 'a' (есть только в s1) и 'e' (есть только в s2).")
print("\nПроизведение s1 * s2:")
print(s1 * s2)
print("ОБЪЯСНЕНИЕ: Та же логика - NaN для несовпадающих индексов.")
print("\nСложение с fill_value=0 (s1.add(s2, fill_value=0)):")
print(s1.add(s2, fill_value=0))
print("ОБЪЯСНЕНИЕ: fill_value=0 заменяет отсутствующие значения на 0, поэтому NaN исчезают.")
print("\n" + "=" * 60)
print("ЗАДАНИЕ 7 ВЫПОЛНЕНО")
print("=" * 60)
# ### Задание 8. Логический отбор
# Комментарий: Логические операции создают булеву маску. Маску можно использовать
# для фильтрации данных. sum() по маске считает количество True.
print("\nЗАДАНИЕ 8: Логический отбор")
print("Исходный Series s6:")
print(s6)
mask = s6 > 30
print("\nМаска (s6 > 30):")
print(mask)
print("\nЭлементы, значение которых > 30 (фильтрация по маске):")
print(s6[mask])
print("\nКоличество элементов > 30 (mask.sum()):", mask.sum())
print("Комментарий: sum() считает True как 1, False как 0")
print("\n" + "=" * 60)
print("ЗАДАНИЕ 8 ВЫПОЛНЕНО")
print("=" * 60)
# ### Задание 9. Переиндексация
# Комментарий: .reindex() меняет индекс Series. Несовпадающие индексы получают NaN.
# fill_value заполняет пропуски. Повторяющиеся индексы дублируют значения.
s = pd.Series([1, 2, 3], index=['x', 'y', 'z'])
print("\nЗАДАНИЕ 9: Переиндексация")
print("Исходный Series s:")
print(s)
print("\n.reindex(['x', 'y', 'w']) - новый индекс, 'w' отсутствует:")
print(s.reindex(['x', 'y', 'w']))
print("Комментарий: Для 'w' получен NaN, т.к. такого индекса не было")
print("\n.reindex(['x', 'y', 'w'], fill_value=0):")
print(s.reindex(['x', 'y', 'w'], fill_value=0))
print("Комментарий: fill_value=0 заполняет отсутствующие значения")
print("\n.reindex(['x', 'y', 'z', 'z']) - с повторяющейся меткой 'z':")
print(s.reindex(['x', 'y', 'z', 'z']))
print("Комментарий: Pandas дублирует значение для каждого вхождения 'z'")
print("\n" + "=" * 60)
print("ЗАДАНИЕ 9 ВЫПОЛНЕНО")
print("=" * 60)
# ### Задание 10. Загрузка данных из CSV и знакомство с DataFrame
# КОММЕНТАРИЙ: Исходная ссылка в задании была неполной и нерабочей.
# Полный URL должен указывать на конкретный CSV файл.
# Однако даже после исправления ссылка может быть недоступна.
print("\nЗАДАНИЕ 10: Загрузка данных из CSV")
print("=" * 60)
print("ВНИМАНИЕ! Задание 10 НЕ ВЫПОЛНЕНО из-за нерабочей ссылки!")
print("Исходная ссылка в задании: 'https://raw.githubusercontent.com/Gewissta/Learning_pandas_russian_translation/master'")
print("Проблема: Это ссылка на директорию, а не на файл CSV.")
print("Даже при попытке указать конкретный файл (GOOG.csv) - ссылка недоступна.")
print("Репозиторий Gewissta/Learning_pandas_russian_translation не существует или закрыт.")
print()
print("Для выполнения задания 10 необходим корректный CSV файл, например:")
print("url = 'https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv'")
print("Или любой другой доступный датасет с данными об акциях.")
print("=" * 60)
# Код, который должен был быть выполнен (закомментирован из-за ошибки):
"""
url = 'https://raw.githubusercontent.com/Gewissta/Learning_pandas_russian_translation/master/GOOG.csv'
df = pd.read_csv(url) # Ошибка! URL не существует
df['Date'] = pd.to_datetime(df['Date'])
df.set_index('Date', inplace=True)
print(df.head())
print(df.info())
"""
print("\n" + "=" * 60)
print("ЗАДАНИЕ 10 ПРОПУЩЕНО (нерабочая ссылка)")
print("=" * 60)
# ### Задание 11 (дополнительное). Простая статистика
# Комментарий: Это задание зависит от данных из задания 10.
# Поскольку данные не загружены, задание 11 также не может быть выполнено.
print("\nЗАДАНИЕ 11: Простая статистика (дополнительное)")
print("=" * 60)
print("ВНИМАНИЕ! Задание 11 НЕ ВЫПОЛНЕНО!")
print("Причина: Задание 11 зависит от DataFrame из задания 10.")
print("Поскольку задание 10 не выполнено (нерабочая ссылка),")
print("нет данных для вычисления статистики и построения графиков.")
print()
print("При наличии данных код должен был выглядеть так:")
print("""
# Средняя цена открытия
mean_open = df['Open'].mean()
# Максимальная цена закрытия
max_close = df['Close'].max()
# Минимальный объем торгов
min_volume = df['Volume'].min()
# Новый столбец Change
df['Change'] = df['Close'] - df['Open']
# Гистограмма изменений
df['Change'].hist()
""")
print("=" * 60)
print("\n" + "=" * 60)
print("ИТОГОВЫЙ ВЫВОД ПО ЛАБОРАТОРНОЙ РАБОТЕ")
print("=" * 60)
print("Успешно выполнены задания 1-9:")
print(" - Импорт библиотек и настройка вывода")
print(" - Создание Series (4 способа)")
print(" - Работа с index и values")
print(" - Создание Series с датами")
print(" - Методы head(), tail(), take()")
print(" - Доступ по метке (loc) и позиции (iloc)")
print(" - Арифметические операции и выравнивание")
print(" - Логический отбор и фильтрация")
print(" - Переиндексация (reindex)")
print()
print("НЕ ВЫПОЛНЕНЫ задания 10 и 11:")
print(" - Причина: нерабочая ссылка на CSV файл")
print(" - Репозиторий Gewissta/Learning_pandas_russian_translation недоступен")
print("=" * 60)
# ---
# ## Ответы на контрольные вопросы
print("\n" + "=" * 60)
print("КОНТРОЛЬНЫЕ ВОПРОСЫ")
print("=" * 60)
print("""
1. Что такое объект Series и чем он отличается от одномерного массива NumPy?
Ответ: Series - это одномерный размеченный массив, где каждому значению соответствует
метка (индекс). Отличие от NumPy array: наличие индекса, поддержка выравнивания
при арифметических операциях, возможность работы с разными типами данных.
2. Для чего используются свойства .index и .values?
Ответ: .index возвращает метки (индексы) Series, .values возвращает массив значений
в виде NumPy array.
3. В чём разница между .loc[] и .iloc[]?
Ответ: .loc[] - доступ по меткам индекса (включает правую границу при срезе).
.iloc[] - доступ по целочисленным позициям (правая граница не включается).
4. Как выполняется выравнивание данных при арифметических операциях над разными Series?
Ответ: Pandas автоматически выравнивает Series по их индексам. Если индекс есть
только в одном из Series, результат содержит NaN для этого индекса.
5. Как использовать логическое выражение для сравнения значений переменных?
Ответ: Логическое выражение (например, series > значение) возвращает булеву маску.
Маску можно использовать для фильтрации: series[маска].
6. Что делает метод .reindex() и когда его удобно применять?
Ответ: .reindex() изменяет индекс Series/DataFrame на новый. Удобно применять
для приведения данных к единой шкале (например, к единому набору дат).
7. Какие параметры функции pd.read_csv() позволяют корректно загрузить столбцы с датами
и сделать его индексом?
Ответ: parse_dates=['Имя_столбца'] для преобразования в дату,
index_col='Имя_столбца' чтобы сделать его индексом.
8. Как построить простой график временного ряда с помощью Pandas?
Ответ: df['столбец'].plot() или df.plot(y='столбец'). Pandas автоматически
подписывает ось X значениями индекса (если он - DatetimeIndex).
""")
print("=" * 60)