"""
# import
"""
# This Python 3 environment comes with many helpful analytics libraries installed
# It is defined by the kaggle/python docker image: https://github.com/kaggle/docker-python
# For example, here's several helpful packages to load in 

import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)

import matplotlib.pyplot as plt
import seaborn as sns 
%matplotlib inline

# Загружаем специальный удобный инструмент для разделения датасета:
from sklearn.model_selection import train_test_split

# Input data files are available in the "../input/" directory.
# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory

import os
for dirname, _, filenames in os.walk('/kaggle/input'):
    for filename in filenames:
        print(os.path.join(dirname, filename))

# Any results you write to the current directory are saved as output.
# всегда фиксируйте RANDOM_SEED, чтобы ваши эксперименты были воспроизводимы!
RANDOM_SEED = 42
# зафиксируем версию пакетов, чтобы эксперименты были воспроизводимы:
!pip freeze > requirements.txt
"""
# DATA
"""
DATA_DIR = '/kaggle/input/sf-dst-restaurant-rating/'
df_train = pd.read_csv(DATA_DIR+'/main_task.csv')
df_test = pd.read_csv(DATA_DIR+'kaggle_task.csv')
sample_submission = pd.read_csv(DATA_DIR+'/sample_submission.csv')
df_cities = pd.read_csv('../input/world-cities/worldcities.csv')
df_cost=pd.read_csv('/kaggle/input/2020-cost-of-living/cost of living 2020.csv')
df_cities.info()
df_cost.info()
df_train.info()
df_train.head(5)
df_test.info()
df_test.head(5)
sample_submission.head(5)
sample_submission.info()
# ВАЖНО! дря корректной обработки признаков объединяем трейн и тест в один датасет
df_train['sample'] = 1 # помечаем где у нас трейн
df_test['sample'] = 0 # помечаем где у нас тест
df_test['Rating'] = 0 # в тесте у нас нет значения Rating, мы его должны предсказать, по этому пока просто заполняем нулями

data = df_test.append(df_train, sort=False).reset_index(drop=True) # объединяем
data.info()
"""
Подробнее по признакам:
* City: Город 
* Cuisine Style: Кухня
* Ranking: Ранг ресторана относительно других ресторанов в этом городе
* Price Range: Цены в ресторане в 3 категориях
* Number of Reviews: Количество отзывов
* Reviews: 2 последних отзыва и даты этих отзывов
* URL_TA: страница ресторана на 'www.tripadvisor.com' 
* ID_TA: ID ресторана в TripAdvisor
* Rating: Рейтинг ресторана
"""
data.sample(5)
data.Reviews[1]
"""
Как видим, большинство признаков у нас требует очистки и предварительной обработки.
"""
"""
## 1. Обработка NAN 
И так, пропуски есть в следующих столбцах:
* Cuisine Style
* Price Range
* Number of Reviews
* Reviews
"""
"""
Какие варианты кухни самые популярные?
"""
data['Cuisine Style']=data['Cuisine Style'].fillna("['no_data']") #заменяем пропуск техническим сообщением
def make_a_list (line):
    line=line[1:-1]
    line=line.replace("'", "")
    line=line.split(', ')
    return (line)
cousine_list=[]
data['Cuisine Style']=data['Cuisine Style'].apply(make_a_list)
data['Cuisine Style'].apply(lambda x: cousine_list.extend(x))
from collections import Counter
Counter(cousine_list).most_common(3)
"""
Заменяем 'no_data' на два самых популярных значения
"""
def replacement (line):
    if line == ['no_data']:
        return ['European', 'Vegetarian Friendly']
    else:
        return line
data['Cuisine Style']=data['Cuisine Style'].apply(replacement)
cousine_list=[]
data['Cuisine Style'].apply(lambda x: cousine_list.extend(x))
Counter(cousine_list).most_common(5)
"""
Самая распространённая ценовая категория:
"""
data['Price Range'].value_counts()
"""
Средняя ценовая категория самая распространённая. На нее и заменим пропуски.
"""
data['Price Range']=data['Price Range'].fillna('$$ - $$$')
"""
Что с количеством отзывов? Посмотрим на распределение. Найдём среднее, медиану и моду.
"""
data['Number of Reviews'].hist()
"""
Распределение не похоже на нормальное. Возможно, потом придётся очищать данные от выбросов.
"""
print(data['Number of Reviews'].mean(), data['Number of Reviews'].median())
data['Number of Reviews'].value_counts(ascending=False)
"""
Пропущенных значений не так уж и много. Для начала попробуем заполнить пропуски модой - нулевым значением.
"""
data['Number of Reviews'].fillna(0, inplace=True)
"""
Пропуски, а за одно и пустые значения я пока что заменю технической строкой.
"""
data['Reviews']=data['Reviews'].fillna("[['no_data'], ['no_date']]")
data['Reviews']=data['Reviews'].apply(lambda x: x.replace("[[], []]", "[['no_data'], ['no_date']]"))
"""
### 2. Обработка признаков
Для начала посмотрим какие признаки у нас могут быть категориальными.
"""
data.sample(5)
"""
#### Возьмем следующий признак "Price Range".
"""
def sign_to_range (x):
    if x == "$":
        return 1
    elif x == "$$ - $$$":
        return 2.5
    else:
        return 4
data['Price Range']=data['Price Range'].apply(sign_to_range)
"""
Cuisine Style. После замены пропусков в ячейках этого столбца лежат списки. Добавим Dummie-переменные к наиболее популярным вариантам кухонь. Наименее популярные отнесем к параметру "others".
"""
#Сначала заново сформируем список вариантов кухни.
cousine_list=[]
data['Cuisine Style'].apply(lambda x: cousine_list.extend(x))
#Теперь посмотрим, какие кухни наиболее популярны, а какие - нет.
Counter(cousine_list).most_common(150)
"""
Мы, ведь, не будем спорить с тем, что 'Vegetarian Friendly' - это не то же, что 'Vegan Options', а 'Japanese' - не то же, что 'Sushi'. Не вижу смысла вводить дополнительные опции, чтобы объединять некоторые элементы списка под одним заголовком. А, вот, объединить, n (число может быть изменено при оптимизации) наименее популярных вариантов в один - 'others' - всё же стоит.
"""
top=list(pd.Series(dict((Counter(cousine_list).most_common(70)))).index) #Очень странный костыль, чтобы сформировать множество самых частых кухонь.
others=(list(set(cousine_list)-set(top))) # формируем множество самых редких вариантов
def other(cuisines):
    for cuisine in cuisines:
        if cuisine in others:
            return 'other'
        else:
            return cuisine

data['Cuisine Style']=data['Cuisine Style'].apply(other) # меняем наименее популярные варианты на "other"
dummi_cuisine=pd.get_dummies(data['Cuisine Style'].apply(pd.Series).stack()).sum(level=0) # Создаём сет dummie-переменных
data=pd.concat([data,dummi_cuisine], axis=1) # Соединяем с сетом "data"
data['Cuisine Style']=data['Cuisine Style'].apply(lambda x: len(x)) # В исходные ячейки возвращаем новый признак
data.sample(2) #смотрим, что получилось
"""
Отзывы. Самый сложный с точки зрения обработки формат данных.
"""
data['Reviews']=data['Reviews'].replace("[[], []]", "[['no_review'], ['01/01/2000']]") #Заменяем пустые значения техническими.
rev_dict=set()
def Reviews_reader(line):
    line=line[2:-2]
    line=line.split('], [')
    line[0]=line[0].split(', ')
    line[1]=line[1].split(', ')
   
    for rev in line[0]:
        rev=(rev[1:-1]).lower()
        rev=rev.replace('!','')
        rev=rev.replace('.','')
        rev=rev.split(' ')
        for word in rev:
            rev_dict.add(word)
    return(line)
data['Reviews']=data['Reviews'].apply(Reviews_reader)
rev_dict
word_in_review={'Good':['gusto','nya','bellisimo','dequate','pleasantly','wunderfull','delucious','excellient','picturesque','👍👍','good','great','best','excellent','nice','delicious','lovely','tasty','amazing','fantastic','perfect','wonderful','pleasant','cozy','awesome','yummy','fabulous','cool','fine','brilliant','enjoyable','good!','outstanding','delicious!','charming','affordable','delightful','comfortable', '+','gorgeous','👏👏👏👏👏'],
               'Bad':['weak','only?','grubby','awseome','wash','ameri','weakest','filthy','disasterous','becareful','miserable','foo','bad','poor','stop','worst','disappointing','terrible','overpriced','rude','disappointed','horrible','mediocre','unfriendly','worse','dirty','disappointment','fo','waste','satisfying']}
# У меня получился вот такой словарь эпитетов, встречающихся в отзывах. Видимо, хвалить люди любят всё же больше, чем ругать. Ну, или кто-то что-то накручивает.

def Reviews_counter (line): # вводим функцию для перевода отзывов в численное значение
    count=0
    for word in line[0]:
        if word in word_in_review['Good']:
            count+=1
        elif word in word_in_review['Bad']:
            count-=1
    return(count)
data['Reviews']=data['Reviews'].apply(Reviews_counter)
data.sample(5)
"""
Теперь попробуем найти особые "фишки" городов. Соберём множество городов, на основании этого множества создадим DF, содержащий важные признаки городов.
"""
cityes=set()
data['City'].apply(lambda x: cityes.add(x))
cityes
len(cityes)
"""
И так, у на 31 город. Добавим для городов такие признаки как средняя температура января и июля, количество осадков в год, количество туристов, посещающих город за год. Я брала из из открытых источников.


"""
Jan_temp={'Paris':4.9, 'Stockholm':-2.3, 'London':5.0, 'Berlin':0.7, 'Munich':-0.9, 'Oporto':10,
       'Milan':1.1, 'Bratislava':-0.4, 'Vienna':0.3, 'Rome':8.1, 'Barcelona':8.9, 'Madrid':5.9,
       'Dublin':5.4, 'Brussels':3.3, 'Zurich':0.4, 'Warsaw':-1.8, 'Budapest':-0.4, 'Copenhagen':1.3,
       'Amsterdam':3.3, 'Lyon':2.6, 'Hamburg':1.3, 'Lisbon':11.4, 'Prague':-1.4, 'Oslo':-2.9,
       'Helsinki':-5, 'Edinburgh':4, 'Geneva':1.8, 'Ljubljana':-0.5, 'Athens':10.2,
       'Luxembourg':0.8, 'Krakow':-3.6}
Jul_temp={'Paris':19.4, 'Stockholm':17.9, 'London':18.7, 'Berlin':18.6, 'Munich':17.4, 'Oporto':19.5,
       'Milan':1.1, 'Bratislava':-0.4, 'Vienna':0.3, 'Rome':8.1, 'Barcelona':8.9, 'Madrid':5.9,
       'Dublin':15.3, 'Brussels':17.6, 'Zurich':18.4, 'Warsaw':18.2, 'Budapest':21.2, 'Copenhagen':17.2,
       'Amsterdam':16.5, 'Lyon':21, 'Hamburg':17.3, 'Lisbon':22.4, 'Prague':18.7, 'Oslo':17.1,
       'Helsinki':17, 'Edinburgh':14.8, 'Geneva':19.7, 'Ljubljana':20.4, 'Athens':27.9,
       'Luxembourg':17.4, 'Krakow':-17.9}
tourists={'Paris':19.0, 'Stockholm':2.7, 'London':19.5, 'Berlin':6.2, 'Munich':4.2, 'Oporto':2.8,
       'Milan':6.6, 'Bratislava':1, 'Vienna':6.6, 'Rome':10.3, 'Barcelona':7.0, 'Madrid':5.6,
       'Dublin':5.4, 'Brussels':4.2, 'Zurich':1.5, 'Warsaw':2.8, 'Budapest':4.0, 'Copenhagen':3.2,
       'Amsterdam':8.8, 'Lyon':3.5, 'Hamburg':6.8, 'Lisbon':3.6, 'Prague': 9.1, 'Oslo':0.7,
       'Helsinki':0.4, 'Edinburgh':4.4, 'Geneva':1.3, 'Ljubljana':0.4, 'Athens':0.24,
       'Luxembourg':0.9, 'Krakow':8.1}
rains={'Paris':6.37, 'Stockholm':5.27, 'London':6.21, 'Berlin':5.7, 'Munich':6.22, 'Oporto':11.78,
       'Milan':10.13, 'Bratislava':6.94, 'Vienna':10.31, 'Rome':9.34, 'Barcelona':6.12, 'Madrid':4.5,
       'Dublin':7.67, 'Brussels':7.82, 'Zurich':10.85, 'Warsaw':10.02, 'Budapest':5.64, 'Copenhagen':11.64,
       'Amsterdam':8.05, 'Lyon':7.63, 'Hamburg':7.38, 'Lisbon':6.91, 'Prague': 4.86, 'Oslo':7.40,
       'Helsinki':6.5, 'Edinburgh':7.06, 'Geneva':9.34, 'Ljubljana':12.90, 'Athens':3.97,
       'Luxembourg':8.31, 'Krakow':6.78}

def january_temp_column(C):
    for  city in Jan_temp:
        if city==C:
            return(Jan_temp[city])
    
data['january_temp']=data['City'].apply(january_temp_column)

def july_temp_column(C):
    for  city in Jul_temp:
        if city==C:
            return(Jul_temp[city])
        
data['july_temp']=data['City'].apply(january_temp_column)

def tourist_flow_column(C):
    for  city in tourists:
        if city==C:
            return(tourists[city])
        
data['tourists_flow']=data['City'].apply(tourist_flow_column)

def rain_column(C):
    for  city in tourists:
        if city==C:
            return(rains[city])
data['rains']=data['City'].apply(rain_column)  
"""
Посмотрим на другие источники данных о городах
"""
df_cities.head(3)
"""
Здесь нас интересует, пожалуй, только население.
"""
df_cities=df_cities.drop(['city_ascii','lat','lng','iso2','iso3','admin_name','capital','id'], axis='columns')
df_cities=df_cities.loc[df_cities.city.isin(cityes)]
df_cities
"""
Упс. Кажется, все европейские топонимы есть в США. Придётся очистить фрейм от этих значений.
"""
df_cities=df_cities.loc[df_cities['country']!='United States']
new_city=set()
df_cities.city.apply(lambda x: new_city.add(x))
cityes-new_city #Этих городов нет в списке. Их надо добавить.
df_cities.loc[2586]=['Krakow', 'Poland',779115]
df_cities.loc[2587]=['Oporto', 'Portugal',240000]
df_cities.loc[2588]=['Zurich', 'Germany',1300000]
df_cities.head(35)
df_cost.sample(3)
"""
Здесь интереснее: есть и индекс цен в ресторанах, и "индекс бигмака" и много чего еще. Но, к сожалению, для стран, а не городов. Для начала возьму такие параметры как Индекс цен в ресторанах, индекс бигмака и цену аренды.
"""
df_cost=df_cost.drop(['Rank 2020','Cost of Living Index','Cost of Living Plus Rent Index','Groceries Index','Local Purchasing Power Index','Unnamed: 9'],axis='columns')
df_cost=df_cost.loc[df_cost.Country.isin(df_cities['country'])]
df_cost.sample(5)
#Мержим!
df_cities=df_cities.merge(df_cost, left_on='country',right_on= 'Country', how='inner')
df_cities=df_cities.drop(['country','Country'], axis='columns')
df_cities.sample(3)
# Мержим с большим фреймом
data=data.merge(df_cities, left_on='City',right_on= 'city', how='inner')
data=data.drop(['city','City','URL_TA','ID_TA'],axis='columns')
data.sample(10)
"""
### И один из моих любимых - [корреляция признаков](https://ru.wikipedia.org/wiki/Корреляция)
На этом графике уже сейчас вы сможете заметить, как признаки связаны между собой и с целевой переменной.
"""
plt.rcParams['figure.figsize'] = (15,10)
data_samp=data[['Restaurant_id','Cuisine Style','Ranking','Price Range','Number of Reviews','Reviews','Rating','sample','january_temp','july_temp','tourists_flow','rains','population','Rent Index','Restaurant Price Index','McMeal($)']]
sns.heatmap(data_samp.drop(['sample'], axis=1).corr())
"""
Вообще благодаря визуализации в этом датасете можно узнать много интересных фактов, например:
* где больше Пицерий в Мадриде или Лондоне?
* в каком городе кухня ресторанов более разнообразна?

придумайте свои вопрос и найдите на него ответ в данных)
"""
"""
# Data Preprocessing
Теперь, для удобства и воспроизводимости кода, завернем всю обработку в одну большую функцию.
"""
# на всякий случай, заново подгружаем данные
df_train = pd.read_csv(DATA_DIR+'/main_task.csv')
df_test = pd.read_csv(DATA_DIR+'/kaggle_task.csv')
df_train['sample'] = 1 # помечаем где у нас трейн
df_test['sample'] = 0 # помечаем где у нас тест
df_test['Rating'] = 0 # в тесте у нас нет значения Rating, мы его должны предсказать, по этому пока просто заполняем нулями

data = df_test.append(df_train, sort=False).reset_index(drop=True) # объединяем
data.info()
from sklearn.preprocessing import MinMaxScaler

def preproc_data(df_input):
    
    df_output = df_input.copy()
    
    # ################### 1. Предобработка ############################################################## 
    # убираем не нужные для модели признаки
    df_output.drop(['Restaurant_id','ID_TA','URL_TA'], axis = 1, inplace=True)
    
    
    # ################### 2. NAN ############################################################## 
    # Далее заполняем пропуски, вы можете попробовать заполнением средним или средним по городу и тд...
    df_output['Number of Reviews']=df_output['Number of Reviews'].fillna(0, inplace=True)
    df_output['Cuisine Style']=df_output['Cuisine Style'].fillna("['European', 'Vegetarian Friendly']")
    df_output['Price Range']=data['Price Range'].fillna('$$ - $$$')
    df_output['Reviews']=df_output['Reviews'].replace("[[], []]", "[['no_review'], ['01/01/2000']]") #Потом пригодится
    df_output['Reviews']=df_output['Reviews'].fillna("[['no_review'], ['01/01/2000']]")
    # ################### 3. Encoding ############################################################## 
    # для One-Hot Encoding в pandas есть готовая функция - get_dummies. Особенно радует параметр dummy_na
    #Обрабатываем колонку разброса цен
    def sign_to_range (x):
        if x == "$":
            return 1
        elif x == "$$ - $$$":
            return 2.5
        else:
            return 4
    df_output['Price Range']=df_output['Price Range'].apply(sign_to_range)
    
    #Обрабатываем варианты кухонь
    cousine_list=[]
    df_output['Cuisine Style'].apply(lambda x: cousine_list.extend(x))
    top=list(pd.Series(dict((Counter(cousine_list).most_common(70)))).index) #Очень странный костыль, чтобы сформировать множество самых частых кухонь.
    others=(list(set(cousine_list)-set(top))) # формируем множество самых редких вариантов
    def other(cuisines):
        for cuisine in cuisines:
            if cuisine in others:
                return 'other'
            else:
                return cuisine

    df_output['Cuisine Style']=df_output['Cuisine Style'].apply(other) # меняем наименее популярные варианты на "other"
    dummi_cuisine=pd.get_dummies(df_output['Cuisine Style'].apply(pd.Series).stack()).sum(level=0) # Создаём сет dummie-переменных
    df_output=pd.concat([df_output,dummi_cuisine], axis=1) # Соединяем с сетом 
    df_output['Cuisine Style']=df_output['Cuisine Style'].apply(lambda x: len(x))
    
    # Города. Feature Engineering будет здесь.
    Jan_temp={'Paris':4.9, 'Stockholm':-2.3, 'London':5.0, 'Berlin':0.7, 'Munich':-0.9, 'Oporto':10,
       'Milan':1.1, 'Bratislava':-0.4, 'Vienna':0.3, 'Rome':8.1, 'Barcelona':8.9, 'Madrid':5.9,
       'Dublin':5.4, 'Brussels':3.3, 'Zurich':0.4, 'Warsaw':-1.8, 'Budapest':-0.4, 'Copenhagen':1.3,
       'Amsterdam':3.3, 'Lyon':2.6, 'Hamburg':1.3, 'Lisbon':11.4, 'Prague':-1.4, 'Oslo':-2.9,
       'Helsinki':-5, 'Edinburgh':4, 'Geneva':1.8, 'Ljubljana':-0.5, 'Athens':10.2,
       'Luxembourg':0.8, 'Krakow':-3.6}
    Jul_temp={'Paris':19.4, 'Stockholm':17.9, 'London':18.7, 'Berlin':18.6, 'Munich':17.4, 'Oporto':19.5,
       'Milan':1.1, 'Bratislava':-0.4, 'Vienna':0.3, 'Rome':8.1, 'Barcelona':8.9, 'Madrid':5.9,
       'Dublin':15.3, 'Brussels':17.6, 'Zurich':18.4, 'Warsaw':18.2, 'Budapest':21.2, 'Copenhagen':17.2,
       'Amsterdam':16.5, 'Lyon':21, 'Hamburg':17.3, 'Lisbon':22.4, 'Prague':18.7, 'Oslo':17.1,
       'Helsinki':17, 'Edinburgh':14.8, 'Geneva':19.7, 'Ljubljana':20.4, 'Athens':27.9,
       'Luxembourg':17.4, 'Krakow':-17.9}
    tourists={'Paris':19.0, 'Stockholm':2.7, 'London':19.5, 'Berlin':6.2, 'Munich':4.2, 'Oporto':2.8,
       'Milan':6.6, 'Bratislava':1, 'Vienna':6.6, 'Rome':10.3, 'Barcelona':7.0, 'Madrid':5.6,
       'Dublin':5.4, 'Brussels':4.2, 'Zurich':1.5, 'Warsaw':2.8, 'Budapest':4.0, 'Copenhagen':3.2,
       'Amsterdam':8.8, 'Lyon':3.5, 'Hamburg':6.8, 'Lisbon':3.6, 'Prague': 9.1, 'Oslo':0.7,
       'Helsinki':0.4, 'Edinburgh':4.4, 'Geneva':1.3, 'Ljubljana':0.4, 'Athens':0.24,
       'Luxembourg':0.9, 'Krakow':8.1}
    rains={'Paris':6.37, 'Stockholm':5.27, 'London':6.21, 'Berlin':5.7, 'Munich':6.22, 'Oporto':11.78,
       'Milan':10.13, 'Bratislava':6.94, 'Vienna':10.31, 'Rome':9.34, 'Barcelona':6.12, 'Madrid':4.5,
       'Dublin':7.67, 'Brussels':7.82, 'Zurich':10.85, 'Warsaw':10.02, 'Budapest':5.64, 'Copenhagen':11.64,
       'Amsterdam':8.05, 'Lyon':7.63, 'Hamburg':7.38, 'Lisbon':6.91, 'Prague': 4.86, 'Oslo':7.40,
       'Helsinki':6.5, 'Edinburgh':7.06, 'Geneva':9.34, 'Ljubljana':12.90, 'Athens':3.97,
       'Luxembourg':8.31, 'Krakow':6.78}

    def january_temp_column(C):
        for  city in Jan_temp:
            if city==C:
                return(Jan_temp[city])
    
    df_output['january_temp']=df_output['City'].apply(january_temp_column)

    def july_temp_column(C):
        for  city in Jul_temp:
            if city==C:
                return(Jul_temp[city])
        
    df_output['july_temp']=df_output['City'].apply(january_temp_column)

    def tourist_flow_column(C):
        for  city in tourists:
            if city==C:
                return(tourists[city])
        
    df_output['tourists_flow']=df_output['City'].apply(tourist_flow_column)

    def rain_column(C):
        for  city in tourists:
            if city==C:
                return(rains[city])
    df_output['rains']=df_output['City'].apply(rain_column) 
    df_output=df_output.merge(df_cities, left_on='City',right_on= 'city', how='inner')
   
    # Отзывы. самое сложное.
    word_in_review=word_in_review={'Good':['gusto','nya','bellisimo','dequate','pleasantly','wunderfull','delucious','excellient','picturesque','👍👍','good','great','best','excellent','nice','delicious','lovely','tasty','amazing','fantastic','perfect','wonderful','pleasant','cozy','awesome','yummy','fabulous','cool','fine','brilliant','enjoyable','good!','outstanding','delicious!','charming','affordable','delightful','comfortable', '+','gorgeous','👏👏👏👏👏'],
                                   'Bad':['weak','only?','grubby','awseome','wash','ameri','weakest','filthy','disasterous','becareful','miserable','foo','bad','poor','stop','worst','disappointing','terrible','overpriced','rude','disappointed','horrible','mediocre','unfriendly','worse','dirty','disappointment','fo','waste','satisfying']}
    def Reviews_reader(line):
        line=line[2:-2]
        line=line.split('], [')
        line[0]=line[0].split(', ')
        line[1]=line[1].split(', ')
   
        for rev in line[0]:
            rev=(rev[1:-1]).lower()
            rev=rev.replace('!','')
            rev=rev.replace('.','')
            rev=rev.split(' ')
            
        return(line)
    df_output['Reviews']=df_output['Reviews'].apply(Reviews_reader)

    def Reviews_counter (line): # вводим функцию для перевода отзывов в численное значение
        count=0
        for word in line[0]:
            if word in word_in_review['Good']:
                count+=1
            elif word in word_in_review['Bad']:
                count-=1
        return(count)
    df_output['Reviews']=df_output['Reviews'].apply(Reviews_counter)
    
    # ################### 5. Clean #################################################### 
    # убираем признаки которые еще не успели обработать, 
    # модель на признаках с dtypes "object" обучаться не будет, просто выберим их и удалим
    object_columns = [s for s in df_output.columns if df_output[s].dtypes == 'object']
    df_output.drop(object_columns, axis = 1, inplace=True)
    names = df_output.columns.values
    scaler=MinMaxScaler()
    df_output = pd.DataFrame(scaler.fit_transform(df_output))
    df_output.columns=names
    return  df_output
"""
>По хорошему, можно было бы перевести эту большую функцию в класс и разбить на подфункции (согласно ООП). 
"""
"""
#### Запускаем и проверяем что получилось
"""
df_preproc = preproc_data(data)
df_preproc.sample(10)
df_preproc.info()
# Теперь выделим тестовую часть
train_data = df_preproc.query('sample == 1').drop(['sample'], axis=1)
test_data = df_preproc.query('sample == 0').drop(['sample'], axis=1)

y = train_data.Rating.values            # наш таргет
X = train_data.drop(['Rating'], axis=1)
"""
**Перед тем как отправлять наши данные на обучение, разделим данные на еще один тест и трейн, для валидации. 
Это поможет нам проверить, как хорошо наша модель работает, до отправки submissiona на kaggle.**
"""
# Воспользуемся специальной функцие train_test_split для разбивки тестовых данных
# выделим 20% данных на валидацию (параметр test_size)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=RANDOM_SEED)
# проверяем
test_data.shape, train_data.shape, X.shape, X_train.shape, X_test.shape
"""
# Model 
Сам ML
"""
# Импортируем необходимые библиотеки:
from sklearn.ensemble import RandomForestRegressor # инструмент для создания и обучения модели
from sklearn import metrics # инструменты для оценки точности модели
# Создаём модель (НАСТРОЙКИ НЕ ТРОГАЕМ)
model = RandomForestRegressor(n_estimators=100, verbose=1, n_jobs=-1, random_state=RANDOM_SEED)
# Обучаем модель на тестовом наборе данных
model.fit(X_train, y_train)

# Используем обученную модель для предсказания рейтинга ресторанов в тестовой выборке.
# Предсказанные значения записываем в переменную y_pred
y_pred = model.predict(X_test)
# Сравниваем предсказанные значения (y_pred) с реальными (y_test), и смотрим насколько они в среднем отличаются
# Метрика называется Mean Absolute Error (MAE) и показывает среднее отклонение предсказанных значений от фактических.
print('MAE:', metrics.mean_absolute_error(y_test, y_pred))
# в RandomForestRegressor есть возможность вывести самые важные признаки для модели
plt.rcParams['figure.figsize'] = (10,10)
feat_importances = pd.Series(model.feature_importances_, index=X.columns)
feat_importances.nlargest(15).plot(kind='barh')
"""
# Submission
Если все устраевает - готовим Submission на кагл
"""
test_data.sample(10)
test_data = test_data.drop(['Rating'], axis=1)
sample_submission
predict_submission = model.predict(test_data)
len(predict_submission)
sample_submission['Rating'] = predict_submission[:10000]
sample_submission.to_csv('submission.csv', index=False)
sample_submission.head(10)