Исследовательский анализ данных и разработка функций

Введение

Нет ничего более мощного, чем научиться чему-то новому или вывести навык на новый уровень, просто выполняя упражнения. В этой статье я буду использовать SQL (Postgres), чтобы провести исследовательский анализ и создать преобразованный обучающий набор функций для моей модели машинного обучения.

Хотя Python или R является текущим языком де-факто для науки о данных и машинного обучения, наличие прочной основы в SQL время от времени поможет вам выполнять итерацию быстрее и позволяет быстро исследовать данные. В конце концов, большая часть данных, с которыми мы работаем, находится в реляционной базе данных. Возможность плавного написания SQL-запросов позволит вам с легкостью обрабатывать данные и быстро приступить к анализу.

Цель

Хотя Python был бы моим предпочтительным подходом, я хотел посмотреть, смогу ли я провести весь исследовательский анализ и разработку функций на SQL. Моя цель - показать, насколько мощным и дополнительным может быть SQL для рабочего процесса. Хотя одним из очевидных ограничений SQL является возможность визуализировать данные в исходном формате, вы все равно можете получить большие выгоды, быстро написав запросы и выведя результаты. Давайте начнем!

Моя установка

  • macOS Mojave версии 10.14.6
  • Postgres (PostgreSQL) 12.2
  • PgAdmin 4.21 (инструмент веб-запросов SQL)
  • Psql (Терминальный доступ к базам данных и таблицам)

В моей последующей статье я завершаю свою контролируемую модель классификации с помощью Python и делюсь своим наивысшим баллом, достигнутым на Kaggle's Public Leadership Board. Посмотри здесь!



Исследование

Мое исследование гибели Титаника показало, что женщины и дети имели приоритет над другими при транспортировке пассажиров в спасательные шлюпки. Это понятие будет играть большую роль в том, как я группирую и анализирую набор данных Kaggle.

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

Обычная практика - откладывать тестовый набор данных, забывая о нем, и использовать только обучающий набор данных для вменения и конструирования функций. В этом упражнении я решил объединить тренировку и тест, чтобы у меня было больше данных для прогнозирования выживаемости. Это фиксированная вселенная, и мы не ожидаем поступления новых данных в конвейер прогнозирования.

Для начала я загрузил файлы train.csv и test.csv из Kaggle и импортировал файлы в две таблицы, которые я создал в базе данных Postgres. Затем я объединил две таблицы, чтобы создать свою первую рабочую таблицу (titanic_train_test_raw).

Чтобы вставить записи в свои таблицы, я открыл psql на своем терминале, набрав «psql postgres». Это инструмент командной строки PostgreSQL. Я выполнил следующие две команды копирования psql, чтобы вставить файлы CSV в свои таблицы.

\copy titanic_train_raw from ‘file/path/train.csv’ delimiter ‘,’ csv HEADER;
\copy titanic_test_raw from ‘file/path/test.csv’ delimiter ‘,’ csv HEADER;

Чтобы лучше понять данные, я рассчитал Survival_rate для каждой функции, доступной в обучающем наборе. Я не публикую здесь весь результат, потому что буду делиться им по частям на протяжении всей статьи. Тем не менее, это позволило мне сформировать мысленное представление о том, какие функции стоит копнуть глубже и почувствовать относительную важность каждой функции.

1. Отсутствующие значения

Давайте внимательнее посмотрим на отсутствующие значения и посмотрим, какие потенциальные функции нужно оценить или даже отбросить из-за разреженности. Вычисления недостающих значений выполняются с комбинированными данными поездов и испытаний. Поскольку в тестовых данных отсутствует функция «Выжившие», в объединенном столбце «Выжившие» отсутствуют 31,93% значений. Кроме того, отсутствуют 20,09%, 77,46%, 0,08% и 0,15% соответственно для возрастов, салонов, тарифов и посадки.

2. Общая выживаемость

Из набора данных обучения я вычислил процент выживших и процент невыживших. Из 891 пассажира в данных обучения выжили только 38,38%. Кроме того, этот анализ позволил мне увидеть, что целевой класс не слишком разбалансирован - он составляет примерно 60% / 40%.

3. Выживаемость по полу и возрасту

У пассажиров-женщин была самая высокая вероятность выживания по сравнению с пассажирами-мужчинами. Данные по тренировкам говорят нам, что только по половому признаку выживаемость составляет примерно 74,2% для женщин и 18,89% для мужчин.

Используя чистый SQL, я создал 10 бинов (age_cohort) для группировки разных возрастов. Я не собираюсь точно разделять возрастные группы, поэтому количество ячеек произвольно, но создайте достаточно, чтобы увидеть соответствующие закономерности. И одна из самых важных частей информации исходит из этой точки зрения. В то время как выживаемость женщин оставалась высокой во всех возрастных когортах, выживаемость мужчин в возрасте девяти лет и младше составляла почти 60% (строка 9 в выходных данных). Только по полу выживаемость мужчин составляла менее 20%, но, разделив данные на возрастные когорты, мы видим, что у подгруппы мужчин была относительно более высокая выживаемость - дети.

4. Выживаемость по тарифам и средней стоимости проезда на пассажира

Чтобы быстро получить представление о соотношении цены и выживания, я использовал функцию окна ntile, чтобы равномерно распределить пассажиров по 6 корзинам, и создал статистику для более внимательного изучения. Это быстрый и грязный метод группирования, поскольку одни и те же суммы тарифов могут попадать в разные ячейки, как показано с помощью fare_min и fare_max.

На первый взгляд, Survival_rate будет расти по мере удорожания билетов. В то же время fare_mean и fare_stddev довольно резко переходят с 5-го на 6. Самая высокая цена fare_max составляет 512,3292 доллара, а самая низкая fare_min - 0 долларов. В столбце тарифов слышен некоторый шум. Постоянно утверждают, что выживаемость женщин намного выше, чем выживаемость мужчин. Судя по атрибуту fare_grouping, выживаемость женщин в 2–9 раз выше, чем у мужчин (survival_ratio).

Вскоре я обнаружил, что стоимость проезда представляет собой полную стоимость билета, а не тариф на пассажира. Например, в билете PC 17755 есть четыре пассажира. Cardeza - это богатая семья, путешествующая первым классом со своими двумя сотрудниками, мисс Уорд и мистером Лесурером.

Чтобы понять, сколько стоит проездной, я разделил его на общее количество пассажиров на каждом билете и сгруппировал средние значения по классу Pclass. Изучая особенности билета, я видел семьи, смешанные группы и отдельных лиц, прикрепленных к отдельным билетам. В результате вместо использования [SibSp (братья, сестры и супруги) + ParCh (родители и дети) + 1 (PassengerId)], обычно используемого другими людьми Kaggle для расчета функции family_size, с использованием количества пассажиров в каждом билете генерируется меньшее стандартное отклонение и дало мне уверенность в том, что это позволит получить относительно более точную среднюю стоимость проезда на пассажира.

В среднем, стоимость проезда в первом классе на пассажира составила около 32 долларов, во втором классе - около 12 долларов, а в третьем классе - около 8 долларов. Это усреднение устранило довольно много шума, встроенного в исходную функцию тарифа. И fare_mean, похоже, сильно коррелировал с Pclass. Таким образом, наличие обеих функций, Pclass и fare_per_passenger, может быть избыточным. Позже мы, возможно, захотим отказаться от одного из них во время построения модели.

5. Выживание на основе названия из оригинального названия

Есть 17 уникальных титулов, некоторые из которых обладают высокой выживаемостью; однако большинство из них нечасто. В тренировочных данных только два пассажира с титулом майор и один пассажир с сэром. Чтобы сделать каждую группу актуальной и эффективной для моделирования, я объединил нечастые заголовки в четыре - Mr, Mrs, Miss и Master - и таким образом создал функцию title_grouping. Например, «мадемуазель», что является аббревиатурой от «мадемуазель», превратилось в «мисс».

Название Master интересно тем, что у него относительно высокая выживаемость, а их довольно много в обучающих данных. В этот период мальчики получали это звание до совершеннолетия. Теперь у нас есть способ идентифицировать мальчиков с помощью title_grouping, в то время как все девочки и женщины объединяются в миссис или мисс.

Я подсчитал выживаемость по каждой title_grouping, и цифры очень показательны. Из-за исключения мальчиков из мужской группы выживаемость мужчин снижается. Напротив, выживаемость мальчиков составляет почти 60%. Я закодирую эту функцию в числовые значения во время заключительной работы по подготовке набора данных для обучения.

6. Выживание, основанное на том, чтобы быть женщиной или ребенком

Я наткнулся на статью Криса Деотта о созданной им функции под названием группы женщина-ребенок, основанной на группировании пассажиров по фамилиям и титулам. По сути, он фокусируется на групповом выживании. Имея это в виду, я создал двоичную функцию, отмечающую, является ли каждый пассажир женщиной или мальчиком. Is_woman_child = 1 захватывает всех женщин и детей, а is_woman_child = 0 захватывает всех взрослых мужчин. Этот флаг может быть избыточным для title_grouping и потенциально способствовать переоснащению модели, но давайте посмотрим, как это работает.

7. Выживание зависит от уровня каюты (также известной как палуба)

Некоторые измерения, которые я сделал для этого, показали, что большинство пассажиров третьего класса находились на уровнях F и G кабины. В связи с этим имело смысл более внимательно изучить уровни кабины, хотя 77% значений отсутствовали. Я предположил, что более низкий уровень салона снижает шансы пассажира на выживание.

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

Само по себе Embarked не совсем рассказывает историю выживания, как показано ниже, но я считаю, что его можно использовать для создания новых функций, которые будут иметь относительно более высокую предсказательную силу. Первоначально Титаник остановился в Саутгемптоне, Англия, а затем переместился в Шербур, Франция. Наконец, остановка в Квинстауне, Ирландия, перед отплытием в Нью-Йорк.

Вкратце: у двух пассажиров отсутствуют значения Embarked, но я нашел этих пассажиров, и они оба сели на корабль в Саутгемптоне (Южный), Англия.

Я предположил корреляцию между местом посадки и палубой (cabin_level), на которой находилась каюта. Другими словами, каждое место посадки предназначалось для определенного набора кают, и существовала некоторая форма организации, вокруг которой каюты заполнялись в первую очередь в зависимости от места посадки. Таким образом, если у вас был билет третьего класса, который начинался с буквы F, что указывало на палубу, на которой находилась каюта, вы, скорее всего, сели на корабль, например, из Саутгемптона, Англия.

Преобразование и разработка функций

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

1. is_one_family / is_mix_group / is_alone

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

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

  • Если у данного билета одна фамилия с несколькими пассажирами, это считается одной семьей, где 1 - Истина, а 0 - Ложь (is_one_family). Это в первую очередь для того, чтобы пометить семьи, путешествующие вместе. Таким образом, если пассажир был членом одной семьи билетов, этот флаг помечается как 1.
  • Если в данном билете несколько фамилий и несколько пассажиров, это помечается как смешанная группа (is_mix_group). Например, в билете 1601 7 разных фамилий. Всего существует 67 билетов с двумя и более фамилиями. Я показываю здесь только 12 лучших билетов.

  • Если у данного билета одна фамилия и один пассажир, это пассажир, путешествующий один (is_alone). При более внимательном рассмотрении мужского населения, взрослые пассажиры-мужчины, путешествующие в одиночку, имели самый низкий коэффициент выживаемости (15,57%) по сравнению с взрослыми мужчинами с семьями (17,46%) и мальчиками с семьями (57,5%).

На первый взгляд, взрослые пассажиры мужского пола, не имеющие семей или принадлежащие к смешанной группе, имели наименьшие шансы на выживание. Мы действительно видим некоторые интересные закономерности в этом выводе. У пассажиров-мужчин, путешествующих со своими семьями, был самый высокий уровень выживаемости. Скорее всего, эти пассажиры-мужчины были мальчиками. Я проверю это ниже.

Когда я выделил мальчиков из мужского населения, картина стала намного яснее. Мальчики выживают быстрее, чем взрослые мужчины.

2. family_size

Чтобы определить размер семьи каждого пассажира, логично добавить SibSp, ParCh и текущего пассажира (+1). Однако вместо того, чтобы определять это на уровне пассажира, было бы разумнее определить максимальный размер семьи на основе номера билета и фамилии.

Я придерживаюсь предположения, что семьи путешествовали вместе и, в данном случае, по одному билету. В результате сумма SibSp, ParCh и текущего пассажира, которая представляет размер семьи, сопоставляется с номером и фамилией билета. Я создал сопоставление подзапроса SQL, в котором, если бы у вас был тот же номер билета и фамилия, тогда размер семьи (SibSp + ParCh + 1) был сопоставлен с пассажиром.

Как только логика была собрана, я посмотрел на среднюю выживаемость по размеру семьи и P-классу. В относительно больших семьях выживаемость была ниже. Можно сделать общий вывод, что если вы были частью большой семьи, вы умерли. Кроме того, ты умер, если был один. Поскольку существует всего несколько билетов на четыре или более пассажиров, трудно обобщить выживаемость больших групп. Однако, основываясь на имеющихся данных, стоит посмотреть, имеет ли размер семьи какое-то относительное значение для прогнозирования выживаемости.

3. Создайте первую сводную таблицу функций: titanic_train_test_raw_v2

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

  • Кодирование признака пола как женский = 1 и мужской = 0
  • Извлечь family_name (фамилию) из функции имени
  • Добавление функции title_grouping, о которой я поделился ранее в посте
  • Добавление функции is_woman_child
  • Извлечение уровней кают (палубы) из функции «Каюта»
  • Кодирование встроенной функции как S = 0, C = 1 и Q = 2
  • Добавление функции family_size на основе номера билета и family_name (фамилии)
  • Добавление функций is_one_family, is_mix_group и is_alone
  • Добавление функции fare_per_passenger путем деления тарифа на количество PassengerId, найденных на каждом билете

4. Создайте вторую сводную таблицу функций: titanic_train_test_raw_v3

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

  • Отсутствующие значения возраста. Введите отсутствующие значения возраста (строки 11 и строки 44–52). Я сгруппировал данные по классу Pclass, полу и title_grouping, чтобы рассчитать средний возраст каждой группы. Я выбрал среднее значение, а не медиану, потому что оба набора чисел похожи. Я также рассчитал стандартное отклонение среднего, чтобы изучить изменчивость.
  • Age_bucket: Кроме того, я создал функцию age_bucket (строка 12). Я решил взять непрерывную переменную возраста и разложить их по 7 ячейкам. Поскольку функция "Возраст" содержит оценочные средние значения, может быть лучше преобразовать ее в категориальную функцию, чтобы минимизировать шум в непрерывной функции.
  • Fare_bucket и отсутствующие значения тарифа: я вычислил средний тариф на пассажира на Pclass в ходе своего исследовательского анализа, и было ясно, что цены были привязаны к классу. Я заполнил недостающие значения тарифов с помощью fare_per_passenger, привязанного к Pclass каждого пассажира. В то же время я создал 5 подборок для создания категориальной функции.
  • Title_grouping: четыре метки текстовой строки в этой функции были преобразованы в числовые значения. (Хозяин = 0, Миссис = 1, Мисс = 2 и Мистер = 3)
  • Отсутствующие значения Cabin_level и Cabin level: Мой подход к заполнению недостающих значений cabin_level заключается в подсчете всех пассажиров по Pclass, Embarked и cabin_level, где Cabin не равно нулю. Затем я оценил в порядке убывания количество пассажиров, прикрепленных к Pclass, Embarked и cabin_level, и для каждой комбинации Pclass + Embarked я взял cabin_level с наибольшим количеством пассажиров, прикрепленных к нему.

Если Pclass = 1 и Embarked = 0, то cabin_level = C.

Если Pclass = 1 и Embarked = 1, то cabin_level = C.

Если Pclass = 1 и Embarked = 2, то cabin_level = C.

Если Pclass = 2 и Embarked = 0, то cabin_level = F.

Если Pclass = 3 и Embarked = 0, то cabin_level = G.

И так далее!

5. Построение модифицированной логики типа «женщина-ребенок»

Я пошел дальше и попытался воссоздать фичу Криса Деотта с некоторыми изменениями в SQL. Я расскажу о том, что я сделал, и в своей последующей статье я буду тестировать эту функцию на эффективность и важность.

  • Во-первых, титул каждого пассажира обозначается как мужчина, женщина или мальчик. Одновременно все мужские титулы помечены как «noGroup», потому что приоритет выживания отдается женщинам и детям.
  • Далее я вычислил частоту фамилий для каждого пассажира. Эта частота проверяет весь набор данных, а не на уровне заявки. И любые пассажиры с частотой фамилии один или меньше, если таковые имеются, помечаются как «noGroup». Таким образом, женщины и дети в семьях имеют приоритет над женщинами, которые путешествуют в одиночку.
  • На этом этапе вероятность выживания рассчитывается с использованием функции «Выжившие» в обучающих данных на основе обновленной функции фамилии. На данный момент большинство фамилий изменилось на «noGroup», и, таким образом, выживаемость всех пассажиров будет обобщена с использованием этой обновленной функции фамилии.
  • Наконец, используя title и surname_survival, я создал новый двоичный флаг title_surname_survival. Эта логика была включена при создании окончательных наборов данных для обучения и тестирования. Я создал две таблицы для захвата каждой - titanic_train_ml_features_v0 и titanic_test_ml_features_v0.

  • Набор данных обучения ML: titanic_train_ml_features_v0

  • Набор данных теста ML: titanic_test_ml_features_v0 (идентичен набору данных обучения, за исключением того, что функция выживших отсутствует.)

Резюме

В этой статье представлено довольно много информации. Я провел свой исследовательский анализ и разработку функций, используя исключительно SQL. Я использовал GROUP BY, оконные функции, функции агрегирования, подзапросы, предложение WITH, предложение HAVING и другие методы SQL для нарезки и нарезки данных.

Следующим шагом является экспорт готовых обучающих и тестовых наборов данных для анализа с помощью Python. В следующей статье я буду использовать Python для тестирования различных моделей машинного обучения, понимания важности функций и настройки выбранной модели с помощью RandomizedSearchCV и GridSearchCV. Наконец, я отправлю свои прогнозы в Kaggle и посмотрю, какое место я занимаю среди других работ!

Вот обзор того, о чем я расскажу в следующей статье:

Название:« Конкурс Kaggle Titanic: построение моделей и настройка на Python »

  • Импортировать библиотеки
  • Подготовка кадров данных для обучения и тестирования
  • Матрица коэффициентов корреляции
  • Создание вспомогательной функции: вывод статистики модели
  • Множественные подогнанные модели и наилучшая подходящая модель
  • Создание вспомогательной функции: рейтинг важности выходных радиочастотных характеристик
  • Выбор функций с учетом важности случайных функций леса, важности перестановок и иерархической кластеризации
  • RandomizedSearchCV: Классификатор случайного леса
  • GridSearchCV: Классификатор случайных лесов
  • Заключение: последние результаты и заключительные мысли

Если у вас есть вопросы, комментарии или отзывы, дайте мне знать. Спасибо!