<jupyter_start><jupyter_text>Семинар по библиотеке Pandas Библиотека Pandas очень полезный инструмент, используемый для первичной работы с данными, с помощью которого можно проводить простой анализ данных и предобработку данных.  Основные возможности Pandas:* удобное чтение и запись данных из csv, txt, xls, SQL databases, HDF5* удобное чтение и запись данных из csv, txt, xls, SQL databases, HDF5* удобная работа с пропусками в данных* поиск, сортировка, выборка объектов, удовлетворяющих заданным критериям* возможности по соединению датасетов* красивая визуализация  Импорт библиотеки:<jupyter_code>import pandas as pd<jupyter_output><empty_output><jupyter_text>Загрузка данных и создание датафреймов  Наиболее популярные форматы данных (при скачивании датасета из интернета):* csv (comma separated file), tsv (tab separated file) - таблицы, записанные в текстовые файлы с простой структурой. Эти файлы можно открывать в обычном текстовом редакторе. Pandas позволяет считывать эти данные именно в формате таблицы.* xls (eXceL Spreadsheet $-$ таблицы Microsoft)* json (JavaScript Object Notation, используется для сериализации структур языка, то есть сохранения сложных объектов, например, вложенных списков или словарей python). Json-текст представляет собой либо набор пар ключ: значение, либо упорядоченный набор значений* txt в иной специфичной для задачи форме (например, vowpal-wabbit и uci bag-of-words для >)В pandas есть функции для считывания во всех этих форматах.В реальной жизни данные хранятся в базах данных, откуда с помощью sql-подобных языков из них составляют файлы в указанных выше форматах.__Чтение из csv с помощью pandas:__ [pandas.read_csv()](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.read_csv.html) У функции есть несколько параметров, основные необходимые:* filepath_or_buffer (перый и единственный обязательный аргумент) --- имя файла* sep $-$ разделитель (; , \t ...)* quotechar $-$ символ кавычек, все что внутри считается за строку (разделители также могут входить в эту строку; ' " ...)* names $-$ список названий колонок* header $-$ номер строки файла (с 0), которую нужно считать заголовком* dtype $-$ словарь, сопоставляющий именам колонок типы данных в них* na_values $-$ строка/список/словарь (ключи $-$ названия колонок) строковых значений, которые нужно считать пропуском.По умолчанию names=None и header=0, то есть названия колонок берутся из первой строки файла. Можно передать названия через names. Если вы не хотите давать названия, укажите header=None, тогда названия будут даны автоматически индексами с 0. Учтите, что названия нужны при дальнейшей работе с данными (если вы только не собираетесь взять оттуда только numpy-матрицу; в этом случае они не понадобятся). Следите за длиной списка названий, он должен совпадать с реальным числом колонок в файле (а в противном случае вы получите ошибки)! Чтобы заменить заголовки, записанные в файле, нужно установить header=0 и передать names.В функцию pd.read_csv можно передавать как путь к файлу, хранящемуся на компьютере, так и ссылку на файл в Интернете.Для чтения xls: [pandas.read_excel](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_excel.html)Для чтения sql: [pandas.read_sql](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_sql.html) Считываем файл с данными, с которыми мы будем работать:<jupyter_code>data = pd.read_csv('rbc_df.txt')
data<jupyter_output><empty_output><jupyter_text>Данные представляют собой 200 наблюдений со страницы РБК Медиа в Инстаграме, включая количество лайков, коммментариев и текст поста. На сегодняшнем семинаре будут изучены основные функции работы с таблицами, потом вы изучите возможности работы со срезами строк, то есть анализ текстов.  Выведем первые пять наблюдений таблицы с помощью метода ___.head()___<jupyter_code>data.head(10)
#автоматически выводит 5, но в скобках можно указать любое число наблюдений<jupyter_output><empty_output><jupyter_text>* Как можно видеть, первый столбец $-$ номера наблюдений, начиная с нуля* Второй - дата поста* Количество комментариев в посте* Количество лайков в посте Посмотреть последние пять строк можно с помощью метода ___.tail()___<jupyter_code>data.tail()
#автоматически выводит 5, но в скобках можно указать любое число наблюдений<jupyter_output><empty_output><jupyter_text>Также можно посмотреть размер таблицы, то есть вывести _(число наблюдений х число признаков)_, используя ___.shape___<jupyter_code>data.shape<jupyter_output><empty_output><jupyter_text>Получается мы имеем 200 наблюдений и в каждом наблюдении по пять признаков: номер наблюдения, дата, число комментариев, число лайков, текст поста Посмотреть тип:<jupyter_code>type(data)<jupyter_output><empty_output><jupyter_text>Как мы можем видеть, мы имеем ___DataFrame___, то есть многомерный массив значений (просто табличка).Также еще одним популярным типом являтся ___Series___. Series – это проиндексированный одномерный массив значений. Каждый столбец DataFrame является типом структуры Series (то есть столбец датафрейма это series)<jupyter_code># Тип одного столбца:
type(data['date'])<jupyter_output><empty_output><jupyter_text>Датафрейм в пандасе можно создавать и вручную:<jupyter_code>df = pd.DataFrame({'col1' : [4,5,6,7], 'col2' : [10,20,30,40], 'col3' : [100,50,-30,-50]})
df
# Посмотреть документацию
?pd.DataFrame<jupyter_output><empty_output><jupyter_text>Работа со строками и столбцами датафрейма Как уже было сказано, датафрейм $-$ это таблица. Можно вывести названия строк (важно, так как иногда полезно в качестве названий строк выбрать какой-то столбец таблицы):<jupyter_code>df.index<jupyter_output><empty_output><jupyter_text>Названия столбцов:<jupyter_code>data.columns<jupyter_output><empty_output><jupyter_text>Полезный функционал:* параметр df.dtypes $-$ типы колонок* метод df.fillna(value), value $-$ на что заменить (скаляр или словарь с ключами-названиями колонок)* методы df.head([N]) и df.tail([N]) $-$ показать N (необязательный аргумент) первых или последних значений* параметры df.index, df.columns и df.values $-$ соответственно индексы строк датафрейма, названия колонок и np.array, составленный из значений датафрейма* метод df.T $-$ транспонировать данные (поменять строки и столбцы местами)* сортировка данных по индексу (по названиям строк) и по значениям колонки, например df.sort_index(axis=1, ascending=False) и df.sort_values(by='B')* метод df.copy() $-$ копировать датафреймВсе структуры данных, показываемые и возвращаемые pandas, имеют тип, придуманный разработчиками pandas (а не стандартный для python список или словарь). Все эти типы имеют удобный интерфейс обращения к своим элементам (индексация, slicing), но иногда кажутся непривычными. Например, df[smth], как указано выше, должен выдать колонку, имеющую название smth (если она существует в датафрейме).В датафрейме могут храниться данные разных типов (главное, чтобы тип был один и тот же внутри колонки), например float, int, string. Выбор одного столбца:<jupyter_code>data['date']<jupyter_output><empty_output><jupyter_text>Выбор нескольких столбцов:<jupyter_code>data[['date', 'comments', 'likes']]<jupyter_output><empty_output><jupyter_text>Другой способ выбирать столбцы:<jupyter_code>data.date<jupyter_output><empty_output><jupyter_text>Тип данных в столбцах:<jupyter_code>data.dtypes<jupyter_output><empty_output><jupyter_text>Если мы бы хотели выделить часть датафрейма без какого-либо столбца, мы бы использовали такой код:Например, для того, чтобы обучить модель на данных, не используя целевую переменную, очевидно<jupyter_code>data
data.drop('Unnamed: 0', axis = 1).head()<jupyter_output><empty_output><jupyter_text>Обратите внимание, что столбец не удалился из датафрейма навсегда. Наоборот, результат нашей операции записан в выводе, и если бы мы хотели его сохранить, мы должны были бы присвоить результат операции новой переменной.Например, вот так:<jupyter_code>data_without_text = data.drop('text', axis = 1).head()
data_without_text.head()
data<jupyter_output><empty_output><jupyter_text>Как можно видеть, столбец не удалился из исходной таблицы, как уже было сказано, однако, в новой таблице его нет, поскольку мы так прописали в коде Индексация по строкам (схоже с обычной индексацией в Python):<jupyter_code>data.iloc[0:7 + 1]<jupyter_output><empty_output><jupyter_text>То есть мы вывели с нулевого наблюдения по седьмое, не включая его<jupyter_code>#индексы как числа
data.iloc[0:7, 0]<jupyter_output><empty_output><jupyter_text>__Задание__Выведите только четные строки, написав условие в квадратных скобках, используя метод ___index___<jupyter_code>#заготовка
data_only_even = data.iloc[data.index % 2 == 0]
data_only_even<jupyter_output><empty_output><jupyter_text>В качестве примера используем какой-то столбец для задания названий строк:Будем использовать копию нашего изначального датафрейма, чтобы не менять его<jupyter_code>data_c = data.copy()
data_c.set_index(data["date"], inplace=True)
data_c.head()<jupyter_output><empty_output><jupyter_text>Теперь строки индексируются с помощью ___date___ (выделено жирным в начале каждой строки) __Задание__Создайте небользой датасет с помощью ___pd.DataFrame___ и сделайте так, чтобы строки индексировались любым из столбцов вашего нового датафрейма<jupyter_code>df = pd.DataFrame({'col1' : [4,5,6,7], 'col2' : [10,20,30,40], 'col3' : [100,50,-30,-50]})
df = df.set_index(df['col1'])
df.drop('col1', axis=1)<jupyter_output><empty_output><jupyter_text>Анализ и преобразование датафрейма Основная информация по датафрейму:<jupyter_code>data
data.drop('Unnamed: 0', axis=1).describe()<jupyter_output><empty_output><jupyter_text>Обратите внимание, что в конце метода describe ставятся скобочки, попробуйте вывести без них :)Выводится количество значений, среднее значение по столбцу, СКО, минимальное значение, максимальное значение и квантилиь Можем вывести уникальные значения в столбце:<jupyter_code>data['date']
set(data['date'])<jupyter_output><empty_output><jupyter_text>Или вот так:<jupyter_code>len(data['date'].unique())<jupyter_output><empty_output><jupyter_text>Уникальные значения в столбце с числом строк с таким значением:<jupyter_code>data['date'].value_counts()<jupyter_output><empty_output><jupyter_text>Функция apply: применение функции поэлементно к столбцу или строке (+ создание нового столбца, потому что apply возвращает результат и никак не модифицирует датафрейм)Сделаем нумерацию наблюдений с единицы а не с нуля:<jupyter_code>def func(x):
    return x+1   
data
data['Number'] = data['Unnamed: 0'].apply(lambda x: x+1)
data['Number2'] = data['Unnamed: 0'] + 1 
data['Number2'] + data['likes']<jupyter_output><empty_output><jupyter_text>Сделаем индексацию строк с помощью столбца Number и удалим столбец Unnamed<jupyter_code>data.set_index(data["Number"], inplace=True)
data.drop('Unnamed: 0', axis = 1)<jupyter_output><empty_output><jupyter_text>Красота! Функция ___groupby___: создание групп по значению какого-то столбца (или группы столбцов)Например выведем среднее значение лайков по датам постов<jupyter_code>data.groupby('date')['likes'].mean()
data.groupby('date')['likes'].mean()
data.groupby('date')['comments'].max()
#Можно сделать с помощью функции:

data.groupby('date')['likes'].apply(lambda x: x.max())<jupyter_output><empty_output><jupyter_text>__Задание__ Выведите сумму комментариев за каждый день<jupyter_code>#Напишите свой код здесь
data.groupby('date')['comments'].sum()<jupyter_output><empty_output><jupyter_text>Сколько было дней, когда сумма лайков за посты за день превышало 10000?<jupyter_code>(data.groupby('date')['likes'].sum() > 10000).sum()
(data.groupby('date')['likes'].sum() > 10000).sum()<jupyter_output><empty_output><jupyter_text>А сколько когда меньше 1000?<jupyter_code>(data.groupby('date')['likes'].sum() < 1000).sum()<jupyter_output><empty_output><jupyter_text>Выбор строк по условию (индексация по строкам по массиву из True и False)Выведем строки за 4 августа 2021, когда количество комментариев было больше 100:<jupyter_code>data[(data.comments > 100) & (data.date == "04/08/2021")]
data[(data.comments > 100) & (data.date == "04/08/2021")]<jupyter_output><empty_output><jupyter_text>Когда меньше 100:<jupyter_code>data[(data.comments < 100) & (data.date == "04/08/2021")]<jupyter_output><empty_output><jupyter_text>Задания  1. Какова доля постов, когда минимальное количество комментариев было больше 100?<jupyter_code>(data.groupby("date")['comments'].min() > 100).sum() / len(data['date'].unique())<jupyter_output><empty_output><jupyter_text>2. Выведите датафрейм за 17/10/21 при условии, что количество лайков за пост меньше 5500<jupyter_code>data[(data.likes < 5500) & (data.date == "17/10/2021")]<jupyter_output><empty_output><jupyter_text>3. Сколько было дней, когда среднее значение количества комментариев за посты за день было меньше 100?<jupyter_code>(data.groupby('date')['comments'].mean() < 100).sum()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09738146885652309, 'fraction_numerical': 0.015029439107530214, 'mean_word_length': 7.487179487179487, 'pattern_counts': {'":': 0, '<': 149, '<?xml version=': 0, '>': 151, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '36', 'dir': 'jupyter-structured-clean-dedup', 'id': '663054', 'license': 'MIT', 'n_tokens_mistral': 5327, 'n_tokens_neox': 6031, 'n_words': 1231, 'path': 'seminars/sem6/Seminar. Pandas.ipynb', 'repo_name': 'hse-python-jour/hse_intro_to_data_journalism', 'size': '12908'}