Открытый курс по машинному обучению. Сессия № 2

</center> Автор материала: программист-исследователь Mail.ru Group, старший преподаватель Факультета Компьютерных Наук ВШЭ Юрий Кашницкий. Материал распространяется на условиях лицензии Creative Commons CC BY-NC-SA 4.0. Можно использовать в любых целях (редактировать, поправлять и брать за основу), кроме коммерческих, но с обязательным упоминанием автора материала.

Тема 1. Первичный анализ данных с Pandas

Практическое задание. Анализ данных пассажиров "Титаника". Решение

Заполните код в клетках (где написано "Ваш код здесь") и ответьте на вопросы в веб-форме.


In [1]:
import numpy as np
import pandas as pd
%matplotlib inline
from matplotlib import pyplot as plt
pd.set_option("display.precision", 2)

Считаем данные из файла в память в виде объекта Pandas.DataFrame


In [2]:
data = pd.read_csv('../data/titanic_train.csv',
                  index_col='PassengerId')

Данные представлены в виде таблицы. Посмотрим на первые 5 строк:


In [3]:
data.head(5)


Out[3]:
Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
PassengerId
1 0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.25 NaN S
2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 0 PC 17599 71.28 C85 C
3 1 3 Heikkinen, Miss. Laina female 26.0 0 0 STON/O2. 3101282 7.92 NaN S
4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 113803 53.10 C123 S
5 0 3 Allen, Mr. William Henry male 35.0 0 0 373450 8.05 NaN S

In [4]:
data.describe()


Out[4]:
Survived Pclass Age SibSp Parch Fare
count 891.00 891.00 714.00 891.00 891.00 891.00
mean 0.38 2.31 29.70 0.52 0.38 32.20
std 0.49 0.84 14.53 1.10 0.81 49.69
min 0.00 1.00 0.42 0.00 0.00 0.00
25% 0.00 2.00 20.12 0.00 0.00 7.91
50% 0.00 3.00 28.00 0.00 0.00 14.45
75% 1.00 3.00 38.00 1.00 0.00 31.00
max 1.00 3.00 80.00 8.00 6.00 512.33

Для примера отберем пассажиров, которые сели в Cherbourg (Embarked=C) и заплатили более 200 у.е. за билет (fare > 200).

Убедитесь, что Вы понимаете, как эта конструкция работает.
Если нет – посмотрите, как вычисляется выражение в квадратных в скобках.


In [5]:
data[(data['Embarked'] == 'C') & (data.Fare > 200)].head()


Out[5]:
Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
PassengerId
119 0 1 Baxter, Mr. Quigg Edmond male 24.0 0 1 PC 17558 247.52 B58 B60 C
259 1 1 Ward, Miss. Anna female 35.0 0 0 PC 17755 512.33 NaN C
300 1 1 Baxter, Mrs. James (Helene DeLaudeniere Chaput) female 50.0 0 1 PC 17558 247.52 B58 B60 C
312 1 1 Ryerson, Miss. Emily Borie female 18.0 2 2 PC 17608 262.38 B57 B59 B63 B66 C
378 0 1 Widener, Mr. Harry Elkins male 27.0 0 2 113503 211.50 C82 C

Можно отсортировать этих людей по убыванию платы за билет.


In [6]:
data[(data['Embarked'] == 'C') & 
     (data['Fare'] > 200)].sort_values(by='Fare',
                               ascending=False).head()


Out[6]:
Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
PassengerId
259 1 1 Ward, Miss. Anna female 35.0 0 0 PC 17755 512.33 NaN C
680 1 1 Cardeza, Mr. Thomas Drake Martinez male 36.0 0 1 PC 17755 512.33 B51 B53 B55 C
738 1 1 Lesurer, Mr. Gustave J male 35.0 0 0 PC 17755 512.33 B101 C
312 1 1 Ryerson, Miss. Emily Borie female 18.0 2 2 PC 17608 262.38 B57 B59 B63 B66 C
743 1 1 Ryerson, Miss. Susan Parker "Suzette" female 21.0 2 2 PC 17608 262.38 B57 B59 B63 B66 C

Пример создания признака.


In [7]:
def age_category(age):
    '''
    < 30 -> 1
    >= 30, <55 -> 2
    >= 55 -> 3
    '''
    if age < 30:
        return 1
    elif age < 55:
        return 2
    else:
        return 3

In [8]:
age_categories = [age_category(age) for age in data.Age]
data['Age_category'] = age_categories

Другой способ – через apply.


In [9]:
data['Age_category'] = data['Age'].apply(age_category)

1. Сколько мужчин / женщин находилось на борту?

  • 412 мужчин и 479 женщин
  • 314 мужчин и 577 женщин
  • 479 мужчин и 412 женщин
  • 577 мужчин и 314 женщин

In [10]:
print("На борту было {} мужчин и {} женщин.".format(sum(data['Sex'] == 'male'), 
                                            sum(data['Sex'] == 'female')))


На борту было 577 мужчин и 314 женщин.

Проще:


In [11]:
data['Sex'].value_counts()


Out[11]:
male      577
female    314
Name: Sex, dtype: int64

2. Выведите распределение переменной Pclass (социально-экономический статус) и это же распределение, только для мужчин / женщин по отдельности. Сколько было мужчин 2-го класса?

  • 104
  • 108
  • 112
  • 125

In [12]:
pd.crosstab(data['Pclass'], data['Sex'], margins=True)


Out[12]:
Sex female male All
Pclass
1 94 122 216
2 76 108 184
3 144 347 491
All 314 577 891

Можно для красоты и картинку нарисовать.


In [13]:
data['Pclass'].hist(label='all')
data[data['Sex'] == 'male']['Pclass'].hist(color="green", 
                                           label='male')
data[data['Sex'] == 'female']['Pclass'].hist(color="yellow", 
                                             label='female')
plt.title('Distribution by class and gender.')
plt.xlabel('Pclass')
plt.ylabel('Frequency')
plt.legend(loc='upper left');


3. Каковы медиана и стандартное отклонение платежей (Fare)? Округлите до 2 десятичных знаков.

  • Медиана – 14.45, стандартное отклонение – 49.69
  • Медиана – 15.1, стандартное отклонение – 12.15
  • Медиана – 13.15, стандартное отклонение – 35.3
  • Медиана – 17.43, стандартное отклонение – 39.1

In [14]:
print("Median fare: ", round(data['Fare'].median(), 2))
print("Fare std: ", round(data['Fare'].std(), 2))


Median fare:  14.45
Fare std:  49.69

4. Правда ли, что люди моложе 30 лет выживали чаще, чем люди старше 60 лет? Каковы доли выживших в обеих группах?

  • 22.7% среди молодых и 40.6% среди старых
  • 40.6% среди молодых и 22.7% среди старых
  • 35.3% среди молодых и 27.4% среди старых
  • 27.4% среди молодых и 35.3% среди старых

In [15]:
young_survived = data[data['Age'] < 30]['Survived']
old_survived = data[data['Age'] > 60]['Survived']

print("Доли выживших: \n\t среди молодых {}%, \n\t среди старых {}%.".format(
    round(100 * young_survived.mean(), 1), 
        round(100 * old_survived.mean(), 1)))


Доли выживших: 
	 среди молодых 40.6%, 
	 среди старых 22.7%.

5. Правда ли, что женщины выживали чаще мужчин? Каковы доли выживших в обеих группах?

  • 30.2% среди мужчин и 46.2% среди женщин
  • 35.7% среди мужчин и 74.2% среди женщин
  • 21.1% среди мужчин и 46.2% среди женщин
  • 18.9% среди мужчин и 74.2% среди женщин

In [16]:
male_survived = data[data['Sex'] == 'male']['Survived']
female_survived = data[data['Sex'] == 'female']['Survived']


print("Доли выживыших: \n\t среди женщин {}%, \n\t среди мужчин {}%".format(
    round(100 * female_survived.mean(), 1), round(100 * male_survived.mean(), 1)))


Доли выживыших: 
	 среди женщин 74.2%, 
	 среди мужчин 18.9%

6. Найдите самое популярное имя среди пассажиров Титаника мужского пола?

  • Charles
  • Thomas
  • William
  • John

In [17]:
first_names = data[data['Sex'] == 'male']['Name'].apply(lambda full_name: 
                full_name.split(',')[1].split()[1])
first_names.value_counts().head()


Out[17]:
William    35
John       25
George     14
Charles    13
Thomas     13
Name: Name, dtype: int64

7. Сравните графически распределение стоимости билетов и возраста у спасенных и у погибших. Средний возраст погибших выше, верно?

  • Да
  • Нет

In [18]:
data[data['Survived'] == 1]['Fare'].hist(color="green", 
                                         label='Survived')
data[data['Survived'] == 0]['Fare'].hist(color="red", 
                                         label='Died')
plt.title('Ticket fare for survived and died')
plt.xlabel('Pounds')
plt.ylabel('Frequency')
plt.legend();



In [19]:
data[data['Survived'] == 1]['Age'].hist(color="green", 
                                         label='Survived', alpha=.5)
data[data['Survived'] == 0]['Age'].hist(color="red", 
                                         label='Died', alpha=.5)
plt.title('Age for survived and died')
plt.xlabel('Years')
plt.ylabel('Frequency')
plt.legend();



In [20]:
#!conda install seaborn
import seaborn as sns

In [21]:
sns.boxplot(data['Survived'], data['Age']);


На глаз разницы не видно, посчитаем точно. Видно, что в среднем погибшие были старше.


In [22]:
data.groupby('Survived')['Age'].mean()


Out[22]:
Survived
0    30.63
1    28.34
Name: Age, dtype: float64

8. Как отличается средний возраст мужчин / женщин в зависимости от класса обслуживания? Выберите верные утверждения:

  • В среднем мужчины 1-го класса старше 40 лет
  • В среднем женщины 1-го класса старше 40 лет
  • Мужчины всех классов в среднем старше женщин того же класса
  • В среднем люди в 1-ом классе старше, чем во 2-ом, а те старше представителей 3-го класса

In [23]:
for cl in data['Pclass'].unique():
    for sex in data['Sex'].unique():
        print("Average age for {0} and class {1}: {2}".format(sex, cl, 
            round(data[(data['Sex'] == sex)
                         & (data['Pclass'] == cl)]['Age'].mean(),2)))


Average age for male and class 3: 26.51
Average age for female and class 3: 21.75
Average age for male and class 1: 41.28
Average age for female and class 1: 34.61
Average age for male and class 2: 30.74
Average age for female and class 2: 28.72

Более красивый подход:


In [24]:
for (cl, sex), sub_df in data.groupby(['Pclass', 'Sex']):
    print("Average age for {0} and class {1}: {2}".format(sex, cl,
                                    round(sub_df['Age'].mean(), 2)))


Average age for female and class 1: 34.61
Average age for male and class 1: 41.28
Average age for female and class 2: 28.72
Average age for male and class 2: 30.74
Average age for female and class 3: 21.75
Average age for male and class 3: 26.51

И еще круче:


In [25]:
pd.crosstab(data['Pclass'], data['Sex'], 
            values=data['Age'], aggfunc=np.mean)


Out[25]:
Sex female male
Pclass
1 34.61 41.28
2 28.72 30.74
3 21.75 26.51

In [26]:
sns.boxplot(data['Pclass'], data['Age']);