«Счастье бесплатно». Это фраза, которую часто используют многие люди, когда они призывают кого-то жить своей лучшей жизнью по средствам. Некоторые даже неистово клянутся, что деньги не имеют никакого отношения к счастью. Но действительно ли счастье бесплатно? Гарантируют ли деньги счастье? Какие еще факторы способствуют счастью и в какой степени?

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

Этот проект состоит из четырех основных частей.

1. понимание и очистка данных

2. Анализ и визуализация данных

3. Моделирование и прогнозирование

4. Оценка модели

Давайте погрузимся!

Понимание и очистка данных

Во-первых, нам нужно импортировать необходимые библиотеки и загрузить данные.

# importing python libraries      

import pandas as pd                  # for data manipulation

import numpy as np                   # for mathematical calculations

import seaborn as sns                # for data visualization

import matplotlib.pyplot as plt      # for plotting graphs
plt.style.use('seaborn')             # the seaborn stylesheet will make our plots look neat and pretty.

%matplotlib inline                   
# "%matplotlib inline" ensures commands in cells below the cell that outputs a plot does not affect the plot
    
import warnings                      # to ignore any warnings
warnings.filterwarnings("ignore")

Данные были загружены с веб-сайта Kaggle.

# Loading the data

whr_2015 = pd.read_csv('2015.csv')
whr_2016 = pd.read_csv('2016.csv')
whr_2017 = pd.read_csv('2017.csv')
whr_2018 = pd.read_csv('2018.csv')
whr_2019 = pd.read_csv('2019.csv')

Данные можно предварительно просмотреть, чтобы увидеть количество строк и столбцов в каждом фрейме данных.

При просмотре данных были замечены несоответствия. Столбцы неоднородны по годам; у некоторых даже есть другие имена и варианты написания.

Чтобы решить эту проблему, мы отбросим нерелевантные столбцы, изменим порядок столбцов и переименуем их, чтобы обеспечить единообразие по годам.

# dropping irrelevant columns
# (inplace=True) argument means that changes made to the dataframe remains permanent.

whr_2015.drop(columns=['Standard Error', 'Region', 'Dystopia Residual'], inplace=True)

whr_2016.drop(columns=['Lower Confidence Interval', 'Upper Confidence Interval', 'Region', 'Dystopia Residual'], inplace=True)

whr_2017.drop(columns=['Whisker.high', 'Whisker.low', 'Dystopia.Residual'], inplace=True)

#nothing to drop for 2018 and 2019 dataframe

#  Reordering the columns to achieve uniformity
whr_2015 = whr_2015[['Happiness Rank', 'Country', 'Happiness Score', 'Economy (GDP per Capita)', 'Family', 'Health (Life Expectancy)', 'Freedom','Generosity', 'Trust (Government Corruption)', 'Year']]
whr_2016 = whr_2016[['Happiness Rank', 'Country', 'Happiness Score', 'Economy (GDP per Capita)', 'Family', 'Health (Life Expectancy)', 'Freedom','Generosity', 'Trust (Government Corruption)', 'Year']]
whr_2017 = whr_2017[['Happiness.Rank', 'Country', 'Happiness.Score', 'Economy..GDP.per.Capita.', 'Family', 'Health..Life.Expectancy.', 'Freedom','Generosity', 'Trust..Government.Corruption.', 'Year']]

# whr_2018 and whr_2019 already have the correct order so no need to reorder
# Renaming the columns
new_column_names = ['Happiness Rank', 'Country', 'Happiness Score', 'GDP per capita', 'Social Support', 'Healthy Life Expectancy', 'Freedom to Make Life Choices', 'Generosity', 'Perceptions of Corruption', 'Year']

years = [whr_2015, whr_2016, whr_2017, whr_2018, whr_2019]
for year in years:
    year.columns = new_column_names

Теперь фреймы данных можно объединить в один.

#merging all 5 dataframes into one 

whr_all = [whr_2015, whr_2016, whr_2017, whr_2018, whr_2019]

happiness = pd.concat(whr_all)

happiness.head()

В одном столбце была строка с пропущенным значением, заполненная медианой столбца. О методах вменения читайте здесь.

# filling the empty row with the median of the colunm

median = happiness['Perceptions of Corruption'].median()
#print(median)

happiness['Perceptions of Corruption'].fillna(median, inplace = True)

Анализ и визуализация данных

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

# checking the frequency distribution of the variables

happiness.hist(bins='auto', figsize=(15,15));

Выбросы были проверены с помощью коробчатых диаграмм. Выходные данные показывают, что есть выбросы в функциях «Социальная поддержка», «Щедрость» и «Восприятие коррупции».

# checking for outliers with boxplots
happiness[['Happiness Score', 'GDP per capita', 'Social Support', 'Healthy Life Expectancy', 'Freedom to Make Life Choices', 'Generosity', 'Perceptions of Corruption']].plot(kind='box', subplots=True, layout=(4,4), figsize=(10,8), grid=True);

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

# let's see the correlation between the features using heatmap


plt.figure(figsize=(15, 10))                 # This specifies the size, the bigger the map, the easier we can understand the map

sns.heatmap(happiness.corr())                # This is sufficient but adding the 'annot' argument makes interpretaton easier

sns.heatmap(happiness.corr(), annot = True, vmin=-1, vmax=1, center= 0, cmap= 'coolwarm', linewidths=3, linecolor='black')  # 'annot' helps display the correlation coefficient


plt.show()

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

Далее мы можем использовать диаграмму рассеяния для просмотра взаимосвязи между показателем счастья и ВВП на душу населения, результат показывает, что более богатые страны счастливее.

# lets's further investigate the relationship between happiness score and GDP

happiness_score = happiness['Happiness Score']
gdp= happiness['GDP per capita']

plt.scatter(happiness_score, gdp)
plt.title('Scatter plot of Happiness vs GDP')
plt.xlabel('Happiness Score')
plt.ylabel('GDP per capita')
plt.show()

Дальнейший анализ показал, как страны ранжируются по некоторым характеристикам за разные годы.

# What countries have the highest GDP per capita over the years?
rich_countries = happiness[['Country', 'GDP per capita']].groupby('Country').mean().sort_values(by='GDP per capita', ascending=False)
rich_countries.head()

# Some of the saddest countries on average
sad_countries = happiness[['Country', 'Happiness Rank']].groupby('Country').mean().sort_values(by = 'Happiness Rank', ascending = True)
sad_countries.tail()

# Five countries with the highest life expectancy
healthy_countries = happiness[['Country', 'Healthy Life Expectancy']].sort_values(by='Healthy Life Expectancy', ascending=False)
healthy_countries.head()

# Some countries with extremely low life expectancy
low_life_expectancy = happiness[['Country', 'Healthy Life Expectancy']].sort_values(by='Healthy Life Expectancy', #=False)
low_life_expectancy.tail()

Моделирование и прогноз

Линейная регрессия - это алгоритм машинного обучения, который моделирует целевое значение прогноза на основе независимых переменных. В основном он используется для выяснения взаимосвязи между переменными и прогнозирования. А теперь давайте сделаем прогноз!

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

#  Dropping irrelevant variables
new_happiness = happiness.drop(['Country', 'Happiness Rank', 'Year'], axis=1)
new_happiness.head()
#new_happiness.info()

С помощью корреляционной матрицы мы можем сделать выбор некоторых функций. Будут выбраны функции с наиболее сильной корреляцией с целевой переменной.

Затем мы разбиваем данные на обучающие и тестовые наборы.

# splitting data
from sklearn.model_selection import train_test_split

# features with low corelation has been removed

X = new_happiness[['GDP per capita', 'Social Support', 'Healthy Life Expectancy', 'Freedom to Make Life Choices']]
y = new_happiness['Happiness Score']

# X = features, y = target variable

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 0)

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

# We need to scale the data before feeding it to the model
# To standardize our data, we import the StandardScaler from the sklearn library
from sklearn.preprocessing import StandardScaler 

scale = StandardScaler()

new_happiness = scale.fit_transform(new_happiness)

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

# Training the algorithm

from sklearn.linear_model import LinearRegression

lm = LinearRegression()                   # instantiating the model

lm.fit(X_train, y_train)                      # fitting the model with the training dataset

Ниже представлен фреймворк, который показывает, насколько каждая функция влияет на оценку счастья.

#making a dataframe of the coeffiecients to help us easily determine which variable carries more weight
coefficient = lm.coef_

coefficient_df = pd.DataFrame(list(zip(X.columns, lm.coef_)), columns=['features', 'coefficients'])
coefficient_df

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

Оценка модели

Оценка помогает нам увидеть, насколько хорошо наша модель работает с конкретным набором данных. Среднеквадратичная ошибка (RMSE) использовалась для оценки модели.

from sklearn import metrics
from sklearn.metrics import mean_squared_error as MSE


print('Root Mean Squared Error:', np.sqrt(MSE(y_test, y_pred)))

Модель имеет оценку RSME ~ 0,56. Чем ниже RMSE, тем лучше модель делает прогнозы. Дальнейшая разработка функций может дать лучший результат. Предложения приветствуются.

Заключение

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

Семья также важна, потому что мы видим разумную корреляцию между социальным статусом (также известным как семья) и оценкой счастья, поэтому дорожите своими близкими.

Это не конец пути для этой модели; Я намерен провести дальнейший анализ модели, чтобы сделать ее более точной, не стесняйтесь проверять наличие обновлений в моем Github.

Благодарности

Этот проект посвящен постоянно растущему сообществу SCA. Лучшее сообщество на свете!

Я глубоко признателен организаторам, фасилитаторам и спонсорам программы наставничества SheCodeAfrica, в первую очередь Аде Ндука Ойом; основательнице SCA, за инициативу и добрые слова. Ифеома Око, моя наставница, щедро отдававшая ей время и ресурсы. Onyinye; мой POC. Jeamz.dev за то, что всегда напоминал мне праздновать маленькие победы. Балогун Омоболаджи, Темитоп Бабатола; за ответы на все мои серьезные и тривиальные вопросы. Одемакинде Элиша за то, что научил меня использовать Git и GitHub. Я также ценю вклад моих товарищей, вы, дамы, рок!