Римски императори: изводи от данните
В описанието на блога обещавам, че ще съдържа пътеписи, математика, история и вероятно доста програмиране. Тази статия спазва обещанието като е свързана с три от четерите теми. Като част от един курс за Machine Learning, който посещавах скоро, ми се отвори възможост да ги съединя в едно. Не очаквайте експертни техники в обработката на данни или неочаквани изводи. Правих го като упражнение за да науча използваните инструменти. По - скоро жанра е хумор с реални данни и факти. Тук съм изложил HTML версия на оригиналният ми Jupyter notebook.
Внимание! Продължавайки да четете определено ще се натъкнете на пингвини.
Тук ще се опитам да предскажа колко време ще управлява нов римски император. За целта ще използвам данните за императорите между Август (26г. пр.н.е.) и Теодосий Първи (395г.). Осъзнавам, че вероятно няма да има достатъчно голяма зависимост между данните и продължителноста на управление, но нищо не ми пречи да опитам. Ако имаше данни и за следващите хиляда години римски императори може би положението щеше да е друго, но не :( Благодаря, Gibbon!
В крайна сметка имаме данни, започващи с Battle of Actium
и завършващи с последният император извън двора в Константинопол
Също така, мисля да опитам да предскажа и за няколко от следващите императори, за които ще си въведа данните ръчно. Ако модела покаже някакъв смисъл, разбира се.
Data set-а е от kaggle - линк. Данните трябва да се сложат във файл с име roman-emperors.csv
в директорията, в която е notebook-a.
Но какъв е смисъла от това?¶
Признавам, че шансовете утре да се появи нов римски император са ниски. Но ако все пак стане, ще знаем след колко време империята му ще е "освободена" и "демократиризирана" с помощта на "миротворци", подозрително управляващи танкова и самолети хахаха :D А и в упражнението се говори за работа с данни, а не за полезни резултати.
# Първо няколко библиотеки, които ще ни трябват
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import mglearn
%matplotlib inline
# Сега, ще заредя данните
romans_data = pd.read_csv('roman-emperors.csv', parse_dates=[3,4,8,9], index_col=['Index'])
# Няколко императора, за пример
romans_data[:5]
Какво представляват данните ни? Количество, фийчъри?
interesting_features = [f for f in romans_data.columns if f not in ['Image', 'Verif', 'Notes']]
print('Features Names: {}'.format(interesting_features))
# Ще си филтрирам само нещата, които наистина бих използвал
romans_data = romans_data[interesting_features]
print('Filtered data shape: {}'.format(romans_data.shape))
Значи имаме данни за 68 императора. В 13 колони. Не са много, но само толкова са събрани в data set-а.
Малко поправка на данните¶
След ръчно разглеждане забелязвам няколко неверни неща, които ме дразнят до безкрайност и няма да мога да преглътна.
Rant ON
Изобщо не съм фен на теорията "Лидия ги отрови". Да, наясно съм, че това е популярната теория. Първо, бедната жена е обвиняване за отровянето на всички. Всички! След четвъртият отровен хората не биха я държали наоколо, нали? Второ, Октавиан все пак вече е бил на 75! Колко по - възрастен трябва да станеш за да се смята, че смърт след болест е естествена? На много конспиративни теористи ще им е полезен Occam's razor.
Rant OFF
# Index 1 - Augustus
romans_data.at[1, 'Cause'] = 'Natural Causes'
romans_data.at[1, 'Killer'] = 'Disease'
romans_data[['Name', 'Killer', 'Cause']][romans_data.Name == 'Augustus']
Също така 63г. пр.н.е., а не 62. Кой ги е събирал тези данни и какво е имал против Август? Ето го и самият човек. Прилича ли на някой роден през 62-ра?
Не! Съвсем очевидно си е роден през 63та. Защо ли? Заради прическата!
romans_data.at[1, 'Birth'] = '0063-09-23T00:53:28+00:53'
romans_data.at[1, 'Reign Start'] = '0027-01-16T00:53:28+00:53'
romans_data[['Birth', 'Death', 'Reign Start', 'Reign End']][romans_data.Name == 'Augustus']
И сега вече да ги разгледаме по - подбробно¶
Първо, интересно ми е къде са се раждали тез императори:
birth_cities = romans_data['Birth City']
print('Different cities: {}'.format(len(birth_cities.unique())))
birth_cities.value_counts()
Изглежда има голяма разнообразие на градовете. Тъжното е, че твърде много са NA. Което вероятно ще направи тази колона не особено полезна.
Също така, изненадващо много са родени около днешен Белград (Singidunum) - 8 в Sirmium и 1 в Singidunum. Това си е интересно само по себе си :D Трябва да се отбележи, че Sirmium е бил основният военен град в източната римска имеприя. Очаквано, императорите от него са тясно свързани с армията и вероятно може да има значение за бъдещият им много-вероятно-преждевременен-и-насилствен-край.
И като си говорим за това, как ли са умирали тези императори? Това може да ни подскаже нещо за очакваната продължителност на управление:
romans_data.Cause.value_counts().plot(kind='barh', title='Causes of Death');
Аха! Преждевременен и насилствен край. Къде би била романтиката иначе? Но тук мога да разделя два вида смърти: 'нормални' и 'насилствени'. Насилствени са Assasination
, Execution
и Suicide
. Всички останали - нормални. Слагам самоубийството като насилствена смърт т.к. никой не се самоубива без да има причина и винаги самоубилите са били поставени пред "или го правя сам, или някой го прави за мен много скоро". Оставям Captivity
и Died in Battle
като "нормални" т.к. това си е съвсем очакван начин да умре император и не е свързан с преждевременно прекратяване на управлението му поради вътрешни причини. Има ли смисъл това? Не знам.
violent = romans_data[(romans_data.Cause == 'Assassination') | (romans_data.Cause == 'Suicide') | (romans_data.Cause == 'Execution')]
print('Violent deaths: {} ({:.2f}%)'.format(len(violent), 100 * len(violent) / len(romans_data)))
Всъщност, колко императора не са умерли в края на управленито си?
romans_data[romans_data['Reign End'] != romans_data['Death']][['Name', 'Death', 'Reign End', 'Cause', 'Killer']]
Интересно, Гета е успял да управлява цяла седмица след като е умрял. Браво на него! Възможноста за зомби-император е интригуваща, но малко вероятно. Също така, брат му би бил крайно объркан. Виждам възможност за много интересен роман :D
Ето, не прилича на зомби, нали?
Според мен по - скоро трябва да го броим като умрял в края на управлението си. Втори опит:
romans_data[romans_data['Reign End'] < romans_data['Death']][['Name', 'Death', 'Reign End', 'Cause', 'Killer']]
Вече е по - добре. Браво Деоклициан, зелките са по - яки от насилствена смърт при всички положения :D Пък и кой с всичкият си ще реши да убива толкова страшен човек?
Да разгледаме дистрибуцията на продължителност на управление. Но първо, сещам се че Август (отново той!) е роден и управлява преди новата ера, но датите в data set-а изглеждат все едно е управлявал от 26та до 14та година. Като изключим машина на времето остава варианта просто data set-а да не може да представя дати от преди новата ера заради не добре подбраният стандарт за дати. Ще трябва да се справим с това.
Интересно е и как всички важни събития в този период са се случвали в 00:53:28. Ides of March паста да яде! Ако бях император, определено щях много да внимавам около това време. Може би да се заключвам в добре окрепена стая, на щрек и въоръжен?
Супер извратената часова зона няма да я коментирам.
type(romans_data.at[1, 'Death'])
Ахъм! Датите ни все още са низове. Ще трябва да се направи нещо и за това. Какво ли му е на parse_dates
на read_csv?
from dateutil import parser
from datetime import datetime
date_fields = ['Reign Start', 'Reign End', 'Birth', 'Death']
def parse_date(val):
if isinstance(val, datetime):
return val
if val is np.nan or val == '' or val is None or val == 'nan':
return np.nan
try:
return parser.parse(val)
except:
return np.nan
for date_field in date_fields:
romans_data[date_field] = romans_data[date_field].map(parse_date)
Сега, като всичко вече е истински дати е време да направим нещата с епохите по правилният начин. Някак си неудобно е да се занимавам с отрицателни числа. За тази цел ще преместя календара да започва от раждането на... (барабани...) Август! Така и така това си е доста по - изискано от календар с начало раждането на анонимен еврейски дърворезбар в Сирия. При това леко не с всичкия си и доста религиозен. Друго си е истинско божество като Август, нали така? Това, че всички сметки стават много по - прости е само страничен ефект, честно.
from datetime import timedelta
christ_epoch = parser.parse('0001-01-01T00:00:00+00:53')
td = (romans_data.at[1, 'Birth'] - christ_epoch) - 2 * timedelta(days=365)
reign_age = romans_data.at[1, 'Birth'] - romans_data.at[1, 'Reign Start']
augustus_dates = romans_data[romans_data.Name == 'Augustus'][date_fields]
def redefine_calendar(val):
if val is np.nan or val == np.nan:
return val
return val + td
# Преместваме датите на всички, така че нулева година да е раждането на Август
for date_field in date_fields:
romans_data[date_field] = romans_data[date_field].map(redefine_calendar)
# Поправяме датите на самият Augustus
romans_data.at[1, 'Birth'] = christ_epoch
romans_data.at[1, 'Reign Start'] = christ_epoch + reign_age - 2 * timedelta(days=365)
romans_data[romans_data.Name == 'Augustus'][date_fields]
Wow, това беше трудно! http://www.biblestudentarchives.com/documents/CalculatingBC-AD.pdf
Нека сега разгледаме по колко време са управлявали императорите.
import seaborn as sns
romans_data['DaysInPower'] = (romans_data['Reign End'] - romans_data['Reign Start']).map(lambda x: x.days)
sns.distplot(romans_data.DaysInPower);
Добре, тази картинка показва "скорошен" край, за да допълни преждевременен и насилствен от последната. Лошото е, че разпределението не ми подсказва нищо особено.
Възраст на "изкачване" на трона¶
Звучи съвсем логично, че императорите-деца имат значително по - висок шанс за любимата ни вече внезапна-и-насилствена-смърт. Не бих заложил и на старците да се задържат. Гадна работа. По тази причина си мисля, че колона "възраст на изкачване" ще е полезна.
try:
sns.distplot((romans_data['Reign Start'] - romans_data['Birth']).map(lambda x: x.days / 365));
except ValueError as e:
print("MUCH ERROR! So exception {}: {}".format(type(e), e))
Ох, явно за някои не знаем кога са родени. Тъжна работа! Ще трябва да си измислим дати. Струва ми се добре да предположим, че са станали императори на някакво подобно време, като остналите. И от "започване на управлениет" да изчислим нашата измислена дата на раждане.
romans_data['AgeOfSuccession'] = (romans_data['Reign Start'] - romans_data['Birth']).map(lambda x: x.days / 365)
romans_data['AgeOfSuccession'].describe()
sns.distplot(romans_data['AgeOfSuccession'].fillna(romans_data['AgeOfSuccession'].mean()));
WTF? Някой е станал император на -27!?
romans_data[['Name'] + date_fields][romans_data.AgeOfSuccession < 0]
Хм, това е от промяната на епохата. Не съм се сетил, че и други освен Август може да са родени преди новата ера. Дали съм потроишл данните безвъзвратно?
# Сигурен съм, че и Клавдий е роден пр.н.е.
romans_data[['Name'] + date_fields][romans_data.Name == 'Claudius']
augustus_birth_ad = parser.parse('0063-09-23T00:53:28+00:53')
# Да се върнем в начално състояние
romans_data.at[2, 'Birth'] = christ_epoch + (augustus_birth_ad - parser.parse('0042-11-16T00:53:28+00:58')) - 2 * timedelta(days=365)
romans_data.at[4, 'Birth'] = christ_epoch + (augustus_birth_ad - parser.parse('0010-08-01T00:53:28+00:58')) - 2 * timedelta(days=365)
romans_data[['Name'] + date_fields][romans_data.Name == 'Claudius']
Накрая ще стана доста добър с тези BC-AD сметки хахаха. Сега, да опитаме отново!
romans_data['AgeOfSuccession'] = (romans_data['Reign Start'] - romans_data['Birth']).map(lambda x: x.days / 365)
romans_data['AgeOfSuccession'].describe()
sns.distplot(romans_data['AgeOfSuccession'].fillna(romans_data['AgeOfSuccession'].mean()));
# Така, вече може да си запазим тази колона. Изглежда вeче няма странни неща.
romans_data.AgeOfSuccession = romans_data.AgeOfSuccession.fillna(romans_data.AgeOfSuccession.mean())
Добре, това вече прилича на нещо разумно. Сега, предполагам, че е много вероятно да има връзка между възраста на изкачване на трона и продължителността на управлението.
plt.close()
plt.scatter(romans_data.AgeOfSuccession, romans_data.DaysInPower)
plt.gca().set_xlim(0, 85)
plt.gca().set_ylim(0, 15000)
plt.gca().set_xlabel('Age Of Succession')
plt.gca().set_ylabel('Days in Power');
Ба! Никаква връзка, няма да е толкова лесно :( Значи ще трябва да се надяваме регресията да намери някакъв смисъл сред останалите данни.
# Удобно, от лекция 4 :D
from sklearn.preprocessing import OneHotEncoder, LabelEncoder
encoding_columns = ['Birth City', 'Birth Province', 'Succession', 'Cause', 'Killer', 'Dynasty', 'Era']
romans_nona = romans_data.fillna("")
romans_encoders = {col: LabelEncoder().fit(romans_nona[col]) for col in encoding_columns}
def encode_categorical(data, columns, encoders):
return pd.DataFrame({col: encoders[col].transform(data[col]) for col in columns},
index = data.index)
romans_encoded = encode_categorical(romans_nona, encoding_columns, romans_encoders)
romans_encoded.head(10)
# Сега е време да направим огромната таблица!
one_hot_encoder = OneHotEncoder().fit(romans_encoded)
romans_sparse = one_hot_encoder.transform(romans_encoded)
romans_full = pd.np.concatenate([romans_sparse.todense(), romans_data[['AgeOfSuccession']]], axis=1)
romans_full.shape
Разделяме си данните на няколко множества за трениране и тестване
from sklearn.model_selection import train_test_split
romans_days_in_power = romans_data.DaysInPower
x_train, x_test, y_train, y_test = train_test_split(romans_full,
romans_days_in_power,
test_size=0.3,
random_state=42)
Време е за тъпа линейна регресия. Какво ще стане като имаме толкова повече feature-и от колкото данни, чудя се.
from sklearn.linear_model import LinearRegression
regressor = LinearRegression()
regressor.fit(x_train, y_train)
print('Train set score: {}'.format(regressor.score(x_train, y_train)))
print('Test set score: {}'.format(regressor.score(x_test, y_test)))
Алелуя! Научило ги е на изуст. Как е възможно това за линейна регресия? A -5 е... не особено полезн модел. Освен да опитаме на ново с по - малко фийчъри. Да кажем, че града в който е роден човек и провинцията, в която е роден не ни се виждат много важни.
encoding_columns = ['Succession', 'Cause', 'Killer', 'Dynasty', 'Era']
romans_encoded = encode_categorical(romans_nona, encoding_columns, romans_encoders)
one_hot_encoder = OneHotEncoder().fit(romans_encoded)
romans_sparse = one_hot_encoder.transform(romans_encoded)
romans_full = pd.np.concatenate([romans_sparse.todense(), romans_data[['AgeOfSuccession']]], axis=1)
print(romans_full.shape)
Добре, фийчърите вече са по - малко от данните. Това може да е полезно.
x_train, x_test, y_train, y_test = train_test_split(romans_full,
romans_days_in_power,
test_size=0.3,
random_state=42)
regressor = LinearRegression()
regressor.fit(x_train, y_train)
print('Train set score: {}'.format(regressor.score(x_train, y_train)))
print('Test set score: {}'.format(regressor.score(x_test, y_test)))
Това е известно подобрени! Не знам дали е от значение, но още се борим да се върнем над нулата. Нека пробваме с други фийчъри. Но междувременно това може да си го направим на малка функция.
def try_feature_set(encoding_columns, romans_nona, romans_encoders):
romans_encoded = encode_categorical(romans_nona, encoding_columns, romans_encoders)
one_hot_encoder = OneHotEncoder().fit(romans_encoded)
romans_sparse = one_hot_encoder.transform(romans_encoded)
romans_full_set = pd.np.concatenate([romans_sparse.todense(), romans_data[['AgeOfSuccession']]], axis=1)
x_train, x_test, y_train, y_test = train_test_split(romans_full_set,
romans_days_in_power,
test_size=0.3,
random_state=42)
regressor = LinearRegression()
regressor.fit(x_train, y_train)
return (regressor, regressor.score(x_train, y_train), regressor.score(x_test, y_test))
possible_combinations = [
['Succession', 'Killer'],
['Succession', 'Cause'],
['Succession', 'Cause', 'Killer'],
['Succession', 'Cause', 'Killer', 'Era'],
['Succession', 'Cause', 'Killer', 'Dynasty'],
['Cause', 'Killer', 'Dynasty', 'Era'],
['Cause', 'Killer', 'Dynasty'],
['Cause', 'Killer'],
['Cause', 'Killer', 'Era'],
['Cause'],
['Killer'],
['Succession'],
['Dynasty'],
['Era'],
['Era', 'Killer'],
['Era', 'Cause'],
# Омръзна ми. Това са всички разумни варинати, за които се сещам.
]
for combination in possible_combinations:
_, train_score, test_score = try_feature_set(combination, romans_nona, romans_encoders)
print("--{}--\ntrain: {}\ntest: {}\n".format(', '.join(combination), train_score, test_score))
О! Имаме някакъв напредък. Само с Cause, Killer, Era и AgeOfSuccession имаме вече положителни числа 🎉! Сега ще опитам да върна местата на раждане по един или друг начин.
possible_combinations = [
['Cause', 'Killer', 'Era', 'Birth City'],
['Cause', 'Killer', 'Era', 'Birth Province'],
['Cause', 'Killer', 'Era', 'Birth City', 'Birth Province'],
]
for combination in possible_combinations:
_, train_score, test_score = try_feature_set(combination, romans_nona, romans_encoders)
print("--{}--\ntrain: {}\ntest: {}\n".format(', '.join(combination), train_score, test_score))
Не, не помагат изобщо! Но още си спомням теорията си за роден във военен град или не. Според мен си струва да се опита.
def emperor_type_by_birth_place(birth_place):
if birth_place in ['Sirmium', 'Singidunum']:
return 'Soldier'
if birth_place in ['Rome', 'Constantinople']:
return 'Patrician'
return 'Pleb'
romans_data['EmperorType'] = romans_data['Birth City'].map(emperor_type_by_birth_place)
romans_nona = romans_data.fillna("")
encoding_columns += ['EmperorType']
romans_encoders = {col: LabelEncoder().fit(romans_nona[col]) for col in encoding_columns}
possible_combinations = [
['EmperorType'],
['Cause', 'EmperorType'],
['Cause', 'Era', 'EmperorType'],
['Killer', 'EmperorType'],
['Killer', 'Era', 'EmperorType'],
['Cause', 'Killer', 'EmperorType'],
['Cause', 'Killer', 'Era', 'EmperorType'],
['Cause', 'Killer', 'Dynasty', 'EmperorType'],
['Cause', 'Killer', 'Dynasty', 'Era', 'EmperorType'],
]
for combination in possible_combinations:
_, train_score, test_score = try_feature_set(combination, romans_nona, romans_encoders)
print("--{}--\ntrain: {}\ntest: {}\n".format(', '.join(combination), train_score, test_score))
Единственият извод, който мога да си направя тук е: колкото по - малко фийчъри, толкова по - добре :D За това нека се върнем няколко стъпки назад.
# Още на графиката си личеше, че няма да стане. Но да го сравним с останалите резултати.
x_train, x_test, y_train, y_test = train_test_split(romans_data[['AgeOfSuccession']],
romans_days_in_power,
test_size=0.3,
random_state=42)
regressor = LinearRegression()
regressor.fit(x_train, y_train)
print('Train set score: {}'.format(regressor.score(x_train, y_train)))
print('Test set score: {}'.format(regressor.score(x_test, y_test)))
Хм. Имма друга идея. Струва ми се логично, че времето което е управлявал прединият император може да има значение за текущият. Така, ако времената са лоши е по - вероятно императорът да не се задържи т.к. се е качил на трона по време на граждански войни, несигурност и други неприятни фактори. Лоши времена биха били 69AD - годината на четерите императора, 193AD - годината на петте императора, 238AD - годината на шестте императора и т.н. Забелязва се как Х
в "Годината на X императора" продължава да расте за известно време. Със сигурност на римляните по това време хич не им е било забавно!
romans_with_prev = romans_data.copy()
romans_with_prev.PreviousEmperorRule = romans_with_prev.DaysInPower
# Възползваме се от това, че индексите на императорите са поредни. Не съвсем, когато става въпрос за такива
# управляващи по едно и също време, но пак е достатъчно близо.
for ind in range(2, len(romans_data)+1):
prev_ind = ind-1
romans_with_prev.at[ind, 'PreviousEmperorRule'] = romans_data.at[prev_ind, 'DaysInPower']
# Махаме Август т.к. за него нямаме предишен император
romans_with_prev = romans_with_prev.drop(1)
Нека видим дали има някаква очевидна връзка между новата колона и target-а.
plt.close()
plt.scatter(romans_with_prev.PreviousEmperorRule, romans_with_prev.DaysInPower)
plt.gca().set_xlim(0, 15000)
plt.gca().set_ylim(0, 15000)
plt.gca().set_xlabel('Prev Days in Power')
plt.gca().set_ylabel('Days in Power');
Не мога да кажа, че виждам особено сериозна връзка.
romans_nona = romans_with_prev.fillna("")
romans_encoded = encode_categorical(romans_nona, encoding_columns, romans_encoders)
with_prev_encoder_one_hot = OneHotEncoder().fit(romans_encoded)
romans_sparse = with_prev_encoder_one_hot.transform(romans_encoded)
romans_full_set = pd.np.concatenate([romans_sparse.todense(), romans_with_prev[['AgeOfSuccession', 'PreviousEmperorRule']]], axis=1)
romans_with_prev_full = romans_full_set.copy()
x_train, x_test, y_train, y_test = train_test_split(romans_full_set,
romans_with_prev.DaysInPower,
test_size=0.3,
random_state=42)
regressor = LinearRegression()
regressor.fit(x_train, y_train)
print('Train set score: {}'.format(regressor.score(x_train, y_train)))
print('Test set score: {}'.format(regressor.score(x_test, y_test)))
Отново много лош резултат. Т.к. отново използваме много feature-и. Да ги намаилм малкото отново. Спомням си, че от преди малко най - добрите опити бяха с (Cause, Killer), (Cause, Killer, Era), (Cause,), (Killer,). Ще ги опитам отново, но с добавени PreviousEmperorRule.
concatenated = ['AgeOfSuccession', 'PreviousEmperorRule']
possible_combinations = [
['Cause', 'Killer'],
['Cause', 'Killer', 'Era'],
['Cause'],
['Killer'],
]
for encoding_columns in possible_combinations:
romans_encoded = encode_categorical(romans_nona, encoding_columns, romans_encoders)
one_hot_encoder = OneHotEncoder().fit(romans_encoded)
romans_sparse = one_hot_encoder.transform(romans_encoded)
romans_full_set = pd.np.concatenate([romans_sparse.todense(), romans_with_prev[concatenated]], axis=1)
x_train, x_test, y_train, y_test = train_test_split(romans_full_set,
romans_with_prev.DaysInPower,
test_size=0.3,
random_state=42)
regressor = LinearRegression()
regressor.fit(x_train, y_train)
print("Columns: {}".format(encoding_columns + concatenated))
print("Train set score: {}".format(regressor.score(x_train, y_train)))
print("Test set score: {}\n".format(regressor.score(x_test, y_test)))
Други алгоритми¶
Мисля, че дойде времето да се опита с нещо различно от линейна регресия. Вече нямам никакви нови идеи. Първо, вижда ми се логично, че kNN може да е добър aлгоритъм за намиране на модел т.к. ако император е поставен в подобна ситуация очакваме да има подобни резултати и продължителност на управление. Нали така?
from sklearn.neighbors import KNeighborsRegressor
x_train, x_test, y_train, y_test = train_test_split(romans_with_prev_full,
romans_with_prev.DaysInPower,
test_size=0.3,
random_state=42)
for n in range(1, 6):
regressor = KNeighborsRegressor(n_neighbors=n)
regressor.fit(x_train, y_train)
print("{} nieghtbours".format(n))
print("Train set score: {}".format(regressor.score(x_train, y_train)))
print("Test set score: {}\n".format(regressor.score(x_test, y_test)))
По - добре от първоначалните опити с линейна регресия, но все още крайно недостатъчно. Може би random forest т.к. лесно ще се отърве от излишните данни?
from sklearn.ensemble import RandomForestRegressor
for e in [5, 10, 100, 200, 500, 1000]:
regressor = RandomForestRegressor(n_estimators=e, n_jobs=3, random_state=42)
regressor.fit(x_train, y_train)
print("{} n_estimators".format(e))
print("Train set score: {}".format(regressor.score(x_train, y_train)))
print("Test set score: {}\n".format(regressor.score(x_test, y_test)))
Яко overfitting. Интересно ми е, какво ли си мисли, че са най - полезните фийчъри?
# Взимам си най - хубавото дърво
regressor = RandomForestRegressor(n_estimators=e, n_jobs=3, random_state=200)
regressor.fit(x_train, y_train)
features_count = romans_with_prev_full.shape[1]
importances = regressor.feature_importances_
std = np.std([tree.feature_importances_ for tree in regressor.estimators_],
axis=0)
indices = np.argsort(importances)[::-1]
# Print the feature ranking
print("Feature ranking:")
for f in range(min(6, features_count)):
print("%d. feature %d (%f)" % (f + 1, indices[f], importances[indices[f]]))
# Plot the feature importances of the forest
plt.close()
plt.figure(figsize=(14, 14), dpi=80)
plt.title("Feature importances")
plt.barh(range(features_count), importances[indices],
color="r", xerr=std[indices], align="center")
plt.yticks(range(features_count), indices)
plt.ylim([-1, features_count])
plt.show()
Подозирам, че feature 43 и 42 са PreviousEmperorRule и AgeOfSuccession. От тази гледна точка random forest-а се съгласява с мен, че са важни :) Но всъщност не знам как да го проверя. Как да обърна тези feature index-и отново към имената на фийчърите? Ясно е, че за много от тях след OneHot ще са от типа неща от сорта "IsDominatе", "IsPrincipate" и т.н.
Това нищо съществено не ми подсказва. Може би, както предполагах, няма никаква реална зависимост между тези данни и продължителноста на управление на император. Мисля да спра до тук. Но имам и няколко други идеи:
- Да се направят bin-ове с деситилетията и да се добави всички дати на императорите в някакъв bin
- Да се опита със SVM т.к. уж те се справяли когато имаш много feature-и малко данни
В крайна сметка не можахме да разберем нищо интересно за императорите. Но се надявам, че самото пътуване си е струвало, а не крайната цел!