70 KiB
Вариант: Список людей.
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
from imblearn.over_sampling import RandomOverSampler
from imblearn.under_sampling import RandomUnderSampler
from sklearn.preprocessing import OneHotEncoder
import numpy as np
import featuretools as ft
# Функция для применения oversampling
def apply_oversampling(X, y):
oversampler = RandomOverSampler(random_state=42)
X_resampled, y_resampled = oversampler.fit_resample(X, y)
return X_resampled, y_resampled
# Функция для применения undersampling
def apply_undersampling(X, y):
undersampler = RandomUnderSampler(random_state=42)
X_resampled, y_resampled = undersampler.fit_resample(X, y)
return X_resampled, y_resampled
def split_stratified_into_train_val_test(
df_input,
stratify_colname="y",
frac_train=0.6,
frac_val=0.15,
frac_test=0.25,
random_state=None,
):
"""
Splits a Pandas dataframe into three subsets (train, val, and test)
following fractional ratios provided by the user, where each subset is
stratified by the values in a specific column (that is, each subset has
the same relative frequency of the values in the column). It performs this
splitting by running train_test_split() twice.
Parameters
----------
df_input : Pandas dataframe
Input dataframe to be split.
stratify_colname : str
The name of the column that will be used for stratification. Usually
this column would be for the label.
frac_train : float
frac_val : float
frac_test : float
The ratios with which the dataframe will be split into train, val, and
test data. The values should be expressed as float fractions and should
sum to 1.0.
random_state : int, None, or RandomStateInstance
Value to be passed to train_test_split().
Returns
-------
df_train, df_val, df_test :
Dataframes containing the three splits.
"""
if frac_train + frac_val + frac_test != 1.0:
raise ValueError(
"fractions %f, %f, %f do not add up to 1.0"
% (frac_train, frac_val, frac_test)
)
if stratify_colname not in df_input.columns:
raise ValueError("%s is not a column in the dataframe" % (stratify_colname))
X = df_input # Contains all columns.
y = df_input[
[stratify_colname]
] # Dataframe of just the column on which to stratify.
# Split original dataframe into train and temp dataframes.
df_train, df_temp, y_train, y_temp = train_test_split(
X, y, stratify=y, test_size=(1.0 - frac_train), random_state=random_state
)
# Split the temp dataframe into val and test dataframes.
relative_frac_test = frac_test / (frac_val + frac_test)
df_val, df_test, y_val, y_test = train_test_split(
df_temp,
y_temp,
stratify=y_temp,
test_size=relative_frac_test,
random_state=random_state,
)
assert len(df_input) == len(df_train) + len(df_val) + len(df_test)
return df_train, df_val, df_test
df = pd.read_csv("../data/age.csv", nrows=100000)
df.info()
Такую информацию могут использовать компании связанные с историей/культурой, с GameDev-ом, с созданием кинематографа. Реальные имена могут сделать тот же фильм более историчным.
Как бизнес-цели выделим следующие 2 варианта: 1) GameDev. Создание игры про конкретного персонажа, живущего в конкретном временном промежутке в конкретной стране. 2) Исследование зависимости длительности жизни от страны проживания.
Поскольку данные не полные, их необходимо заполнить стандартными значениями:
print(df.isnull().sum())
df.fillna({"Gender": "NaN", "Country": "NaN", "Occupation" : "NaN", "Manner of death" : "NaN"}, inplace=True)
df = df.dropna()
df.info()
df.tail()
Данные приращены, удалены только те строки, в которых не было даты смерти или короткого описания
df.plot.hist(column=["Birth year"], xlim=(1000, 2000), bins=4000)
Помимо этого обработаем колонку страны таким образом, что каждый человек, который жил не в одной стране, будет занимать более одной строки, в соответствии с количеством стран в которых он жил.
df['Country'] = df['Country'].str.split('; ')
df = df.explode('Country')
df.info()
Далее выполним разбиение на обучающую, контрольную и тестовую выборки.
data = df.copy()
value_counts = data["Country"].value_counts()
rare = value_counts[value_counts < 50].index
data = data[~data["Country"].isin(rare)]
print(len(data["Country"].unique()))
df_train, df_val, df_test = split_stratified_into_train_val_test(
data, stratify_colname="Country", frac_train=0.60, frac_val=0.20, frac_test=0.20)
print("Обучающая выборка: ", df_train.shape)
print(df_train["Country"].value_counts())
print("Контрольная выборка: ", df_val.shape)
print(df_val["Country"].value_counts())
print("Тестовая выборка: ", df_test.shape)
print(df_test["Country"].value_counts())
В данных были удалены строки, у которых были "редкие" страны. Данные наращивать не будем, поскольку в этом нет необходимости
Выполним конструирование признаков.
Начнем с унитарного кодирования категориальных признаков. Под этот пункт подходит столбец страна
encoder = OneHotEncoder(sparse_output=False, drop="first")
encoded_values = encoder.fit_transform(data[["Country"]])
encoded_columns = encoder.get_feature_names_out(["Country"])
encoded_values_df = pd.DataFrame(encoded_values, columns=encoded_columns)
encoded_values_df
Далее выполним дискретизацию числовых признаков
labels = ["young", "middle-aged", "old"]
num_bins = 3
hist1, bins1 = np.histogram(data["Age of death"].fillna(data["Age of death"].median()), bins=num_bins)
pd.concat([data["Age of death"], pd.cut(data["Age of death"], list(bins1), labels=labels)], axis=1).head(20)
Выполнить «ручной» синтез признаков в рамках данного набора данных не является возможным.
Масштабирование признаков на основе нормировки и стандартизации в рамках данного набора данных не является необходимым.
Выполним конструирование признаков с применением фреймворка Featuretools.
data1 = data.drop_duplicates(subset="Id", keep="first")
df_train = pd.DataFrame(data1)
# Создание EntitySet
es = ft.EntitySet(id='death_data')
# Добавление DataFrame в EntitySet
es = es.add_dataframe(
dataframe_name='deaths',
dataframe=df_train,
index='Id',
make_index=False
)
# Определение примитивов (операций) для конструирования признаков
feature_matrix, feature_defs = ft.dfs(
entityset=es,
target_dataframe_name='deaths',
max_depth=2,
verbose=1,
n_jobs=1
)
# Вывод сгенерированных признаков
print(feature_matrix.head())
Все наборы признаков имеют плохую предсказательную способность, высокую скорость вычисления, малую надежность, корреляцию и цельность. Они не являются информативными, как и сам набор данных