Путь к данным из Титаника:
url = "https://raw.githubusercontent.com/dm-fedorov/pandas_basic/master/%D0%B1%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5%20%D0%B2%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5%20%D0%B2%20pandas/data/titanic.csv"
Перевод данных из .csv в DataFrame:
import pandas as pd
df = pd.read_csv(url)
df.head(3)
PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 3 | Braund, Mr. Owen Harris | male | 22.0 | 1 | 0 | A/5 21171 | 7.2500 | NaN | S |
1 | 2 | 1 | 1 | Cumings, Mrs. John Bradley (Florence Briggs Th... | female | 38.0 | 1 | 0 | PC 17599 | 71.2833 | C85 | C |
2 | 3 | 1 | 3 | Heikkinen, Miss. Laina | female | 26.0 | 0 | 0 | STON/O2. 3101282 | 7.9250 | NaN | S |
df.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 891 entries, 0 to 890 Data columns (total 12 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 PassengerId 891 non-null int64 1 Survived 891 non-null int64 2 Pclass 891 non-null int64 3 Name 891 non-null object 4 Sex 891 non-null object 5 Age 714 non-null float64 6 SibSp 891 non-null int64 7 Parch 891 non-null int64 8 Ticket 891 non-null object 9 Fare 891 non-null float64 10 Cabin 204 non-null object 11 Embarked 889 non-null object dtypes: float64(2), int64(5), object(5) memory usage: 83.7+ KB
Удаляем лишние столбцы:
df.drop(['PassengerId', 'Name', 'Ticket'],
axis=1,
inplace=True)
df.head(3)
Survived | Pclass | Sex | Age | SibSp | Parch | Fare | Cabin | Embarked | |
---|---|---|---|---|---|---|---|---|---|
0 | 0 | 3 | male | 22.0 | 1 | 0 | 7.2500 | NaN | S |
1 | 1 | 1 | female | 38.0 | 1 | 0 | 71.2833 | C85 | C |
2 | 1 | 3 | female | 26.0 | 0 | 0 | 7.9250 | NaN | S |
Округляем стоимость билета до двух знаков после запятой (так красиво):
df['Fare'] = round(df['Fare'], 2)
df.head(3)
Survived | Pclass | Sex | Age | SibSp | Parch | Fare | Cabin | Embarked | |
---|---|---|---|---|---|---|---|---|---|
0 | 0 | 3 | male | 22.0 | 1 | 0 | 7.25 | NaN | S |
1 | 1 | 1 | female | 38.0 | 1 | 0 | 71.28 | C85 | C |
2 | 1 | 3 | female | 26.0 | 0 | 0 | 7.92 | NaN | S |
Определяем проблемные столбцы (обратите внимание на большое число пропусков в столбце Age
):
df.isna().sum()
Survived 0 Pclass 0 Sex 0 Age 177 SibSp 0 Parch 0 Fare 0 Cabin 687 Embarked 2 dtype: int64
Можно настраивать и изменять способ удаления данных, например с помощью параметра thresh=2
, который оставит строки с более, чем с 2 непустыми значениями:
# df.dropna()
#
# https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.dropna.html
#df['Age'].fillna(25)
Заменить пропущенные значения на cреднее арифметическее по столбцу:
df['Age'] \
.fillna(df['Age'].mean())
0 22.000000 1 38.000000 2 26.000000 3 35.000000 4 35.000000 ... 886 27.000000 887 19.000000 888 29.699118 889 26.000000 890 32.000000 Name: Age, Length: 891, dtype: float64
Заменить пропущенные значения на среднее арифметические в зависимости от класса каюты (Pclass
).
Вычисляем среднее арифметические в зависимости от класса каюты:
df.query("Pclass == 1").Age.mean()
38.233440860215055
df.sample(15)
Survived | Pclass | Sex | Age | SibSp | Parch | Fare | Cabin | Embarked | |
---|---|---|---|---|---|---|---|---|---|
467 | 0 | 1 | male | 56.00 | 0 | 0 | 26.55 | NaN | S |
789 | 0 | 1 | male | 46.00 | 0 | 0 | 79.20 | B82 B84 | C |
341 | 1 | 1 | female | 24.00 | 3 | 2 | 263.00 | C23 C25 C27 | S |
831 | 1 | 2 | male | 0.83 | 1 | 1 | 18.75 | NaN | S |
883 | 0 | 2 | male | 28.00 | 0 | 0 | 10.50 | NaN | S |
675 | 0 | 3 | male | 18.00 | 0 | 0 | 7.78 | NaN | S |
378 | 0 | 3 | male | 20.00 | 0 | 0 | 4.01 | NaN | C |
259 | 1 | 2 | female | 50.00 | 0 | 1 | 26.00 | NaN | S |
552 | 0 | 3 | male | NaN | 0 | 0 | 7.83 | NaN | Q |
874 | 1 | 2 | female | 28.00 | 1 | 0 | 24.00 | NaN | C |
21 | 1 | 2 | male | 34.00 | 0 | 0 | 13.00 | D56 | S |
78 | 1 | 2 | male | 0.83 | 0 | 2 | 29.00 | NaN | S |
526 | 1 | 2 | female | 50.00 | 0 | 0 | 10.50 | NaN | S |
363 | 0 | 3 | male | 35.00 | 0 | 0 | 7.05 | NaN | S |
517 | 0 | 3 | male | NaN | 0 | 0 | 24.15 | NaN | Q |
Пишем функцию, которая принимает на входе строку и просматривает необходимые столбцы:
def fill_age(row):
# проверка, что значение пропущенное
if pd.isnull(row['Age']):
# тогда смотрим на класс каюты
if row['Pclass'] == 1:
# возвращаем среднее значение для первого класса
return df.query("Pclass == 1").Age.mean()
elif row['Pclass'] == 2:
return df.query("Pclass == 2").Age.mean()
elif row['Pclass'] == 3:
return df.query("Pclass == 3").Age.mean()
# возвращаем значение возраста, если оно было задано
return row['Age']
Самый важный момент - применение функции apply
, которая заполняет пропущенные значения указанными в функции fill_age
:
df.apply(fill_age,
axis="columns")
0 22.00000 1 38.00000 2 26.00000 3 35.00000 4 35.00000 ... 886 27.00000 887 19.00000 888 25.14062 889 26.00000 890 32.00000 Length: 891, dtype: float64
Эквивалентен способу 3, но менее очевиден и более короткий:
df \
.groupby('Pclass', group_keys=True)['Age'] \
.apply(lambda x:x.fillna(x.mean()))
Pclass 1 1 38.00000 3 35.00000 6 54.00000 11 58.00000 23 28.00000 ... 3 882 22.00000 884 25.00000 885 39.00000 888 25.14062 890 32.00000 Name: Age, Length: 891, dtype: float64
Проверяем эквивалентность способовов 3 и 4:
(df.apply(fill_age, axis=1)) \
.equals(df.groupby('Pclass', group_keys=True)['Age'] \
.apply(lambda x:x.fillna(x.mean())))
False
Столбец должен содержать значение "alone", если он был на борту один (без супруга/супруги, братьев, сестер, детей и родителей) и значение "not alone", если пассажир путешествовал с кем-то из родственников.
SibSp
- Количество братьев и сестер / супругов на бортуParch
- число родителей / детей на бортус помощью функции и apply
:
def alone_check(row):
# проверяем наличие значения
if row['SibSp'] > 0 or row['Parch'] > 0:
# возвращаем то значение, которым заполняем столбец
return 'not_alone'
return 'alone'
df['Alone'] = df.apply(alone_check, axis=1)
df
Survived | Pclass | Sex | Age | SibSp | Parch | Fare | Cabin | Embarked | Alone | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 3 | male | 22.0 | 1 | 0 | 7.25 | NaN | S | not_alone |
1 | 1 | 1 | female | 38.0 | 1 | 0 | 71.28 | C85 | C | not_alone |
2 | 1 | 3 | female | 26.0 | 0 | 0 | 7.92 | NaN | S | alone |
3 | 1 | 1 | female | 35.0 | 1 | 0 | 53.10 | C123 | S | not_alone |
4 | 0 | 3 | male | 35.0 | 0 | 0 | 8.05 | NaN | S | alone |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
886 | 0 | 2 | male | 27.0 | 0 | 0 | 13.00 | NaN | S | alone |
887 | 1 | 1 | female | 19.0 | 0 | 0 | 30.00 | B42 | S | alone |
888 | 0 | 3 | female | NaN | 1 | 2 | 23.45 | NaN | S | not_alone |
889 | 1 | 1 | male | 26.0 | 0 | 0 | 30.00 | C148 | C | alone |
890 | 0 | 3 | male | 32.0 | 0 | 0 | 7.75 | NaN | Q | alone |
891 rows × 10 columns
с помощью lambda-функции:
df['Alone'] = df.apply(lambda x: 'not_alone' if x['SibSp'] or x['Parch'] > 0 \
else 'alone', axis=1)
df
Survived | Pclass | Sex | Age | SibSp | Parch | Fare | Cabin | Embarked | Alone | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 3 | male | 22.0 | 1 | 0 | 7.25 | NaN | S | not_alone |
1 | 1 | 1 | female | 38.0 | 1 | 0 | 71.28 | C85 | C | not_alone |
2 | 1 | 3 | female | 26.0 | 0 | 0 | 7.92 | NaN | S | alone |
3 | 1 | 1 | female | 35.0 | 1 | 0 | 53.10 | C123 | S | not_alone |
4 | 0 | 3 | male | 35.0 | 0 | 0 | 8.05 | NaN | S | alone |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
886 | 0 | 2 | male | 27.0 | 0 | 0 | 13.00 | NaN | S | alone |
887 | 1 | 1 | female | 19.0 | 0 | 0 | 30.00 | B42 | S | alone |
888 | 0 | 3 | female | NaN | 1 | 2 | 23.45 | NaN | S | not_alone |
889 | 1 | 1 | male | 26.0 | 0 | 0 | 30.00 | C148 | C | alone |
890 | 0 | 3 | male | 32.0 | 0 | 0 | 7.75 | NaN | Q | alone |
891 rows × 10 columns