In [1]:
import pandas as pd
import numpy as np
In [2]:
from matplotlib import pyplot as plt
import seaborn as sns
plt.style.use('bmh')
%matplotlib inline
In [3]:
import random
random.seed(42)
np.random.seed(42)
In [4]:
districts = {1: 'NW', 4: 'C', 5:'N', 6:'NE', 7:'E', 8:'SE', 9:'S', 10:'SW', 11:'W'}
In [5]:
data = pd.read_csv('cian_full_data.csv')
In [6]:
data.head()
Out[6]:
Приведем данные в порядок: удалим первую нулевую строку, восстановим индексы, переименуем колонки
In [7]:
data.drop(0, axis = 0, inplace = True)
In [8]:
data.head()
Out[8]:
In [9]:
data.index = [x for x in range(len(data.index))]
In [10]:
data.rename(columns={'0' : 'Bal', '1': 'Brick', '2': 'Distance', '3': 'Floor', '4': 'Kitsp', '5': 'Livsp', '6': 'Metrdist',
'7': 'New', '8': 'Nfloors', '9': 'Price', '10': 'Rooms', '11': 'Tel', '12': 'Totsp',
'13': 'Walk', '14': 'link'}, inplace = True)
In [11]:
data.head()
Out[11]:
In [12]:
data.shape
Out[12]:
Посмотрим процент пропущенных данных по признакам
In [13]:
def nans_appearance(data):
columns = []
nans = data.isnull()
for i in data.columns:
x = nans[nans[i] == True].shape[0]
if (x > 0):
columns.append(i)
print('nans in', i, ':', x, '; percentage : '"%.3f" %(x/data.shape[0]))
return columns
In [14]:
col_nans = nans_appearance(data)
Число уникальных значений для этих колонок
In [15]:
def my_nunique(data, columns):
for i in columns:
print(str(i) + ' : ' + str(data[i].nunique()))
In [16]:
my_nunique(data, col_nans)
In [17]:
data.drop('Tel', axis = 1, inplace = True)
In [18]:
data[data['Nfloors'].isnull()].shape
Out[18]:
In [19]:
data = data[data['Nfloors'] > 0]
data.index = [x for x in range(len(data.index))]
data.shape
Out[19]:
In [20]:
data['Metrokm'] = np.nan
Учитывая, что средняя скорость пешехода 4 км/ч, а машины 50 км/ч, заполним столбец со значениями предполагаемых километров до метро (помним, что metrdist содержит информацию в минутах)
In [21]:
for i in range(data.shape[0]):
if (data['Walk'][i] == 1.):
data['Metrokm'][i] = 4/60 * data['Metrdist'][i]
elif (data['Walk'][i] == 0.):
data['Metrokm'][i] = 5/6 * data['Metrdist'][i]
Посмотрим процент странных значений и выбросим эти данные, если он будет мал
In [23]:
data[data['Metrokm'] > 17].shape[0]/data.shape[0]
Out[23]:
In [24]:
data = data[(data['Metrokm'] <= 17) | (data['Metrokm'].isnull())]
data.index = [x for x in range(len(data.index))]
Посчитаем среднее значение по округу, заполним пропуски
In [25]:
metrokms_distr=[]
for i in districts.values():
tmp = data[data['Distr'] == i]
med = tmp['Metrokm'].median()
metrokms_distr.append(med)
In [26]:
tmp = []
ind = 0
for i in districts.keys():
tmp.append((districts.get(i), metrokms_distr[ind]))
ind += 1
tmp = dict(tmp)
tmp
Out[26]:
In [27]:
for i in districts.values():
for j in range(data.shape[0]):
if (data['Distr'][j] == i):
if (not(data['Metrokm'][j] >= 0)):
value = tmp.get(i)
data['Metrokm'][j] = value
In [29]:
data.drop(['Metrdist', 'Walk'], axis = 1, inplace = True)
In [30]:
nans_appearance(data)
Out[30]:
In [31]:
totsps = data['Totsp'].unique()
totsps.sort()
In [32]:
print('min =', totsps.min(), '; max =', totsps.max())
In [33]:
count_totsps = []
for i in totsps:
count_totsps.append(data[data['Totsp'] == i].shape[0])
In [34]:
plt.hist(data['Totsp'], color='r')
plt.show()
Посмотрю на то, что кроется за краевыми значениями
In [35]:
data[data['Totsp'] < 18]['link']
Out[35]:
In [36]:
data[data['Totsp'] < 20].shape[0]/data.shape[0]
Out[36]:
In [37]:
data = data[data['Totsp'] >= 20]
data.index = [x for x in range(len(data.index))]
In [38]:
data[data['Totsp'] > 320].shape[0]/data.shape[0]
Out[38]:
In [39]:
data = data[data['Totsp'] < 320]
data.index = [x for x in range(len(data.index))]
In [40]:
plt.hist(data['Totsp'], color='r')
plt.show()
In [41]:
data.shape
Out[41]:
In [42]:
train = data[data['Kitsp'] > 0]
train.shape
Out[42]:
In [43]:
test = data[data['Kitsp'].isnull()]
test.shape
Out[43]:
In [44]:
X = train['Totsp']
X = X.reshape((X.shape[0],1))
Y = train['Kitsp']
Y = Y.reshape((Y.shape[0],1))
X_test = test['Totsp']
X_test = X_test.reshape((X_test.shape[0],1))
In [46]:
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
In [47]:
Xtr, Xval, Ytr, Yval = train_test_split(X, Y, test_size = 0.2)
model_kitchen = LinearRegression(normalize = True)
model_kitchen.fit(Xtr, Ytr)
Out[47]:
In [48]:
pred = model_kitchen.predict(Xval)
In [49]:
from sklearn.metrics import mean_squared_error
from math import sqrt
print('rmse:', sqrt(mean_squared_error(Yval, pred)))
In [50]:
Y_test = model_kitchen.predict(X_test)
ind = 0
for i in range(data.shape[0]):
if (not(data['Kitsp'][i] >= 0)):
data['Kitsp'][i] = Y_test[ind]
ind += 1
In [52]:
nans_appearance(data)
Out[52]:
In [53]:
train = data[data['Livsp'] > 0]
train.shape
Out[53]:
In [54]:
test = data[data['Livsp'].isnull()]
test.shape
Out[54]:
In [55]:
X = train['Totsp']
X = X.reshape((X.shape[0],1))
Y = train['Livsp']
Y = Y.reshape((Y.shape[0],1))
X_test = test['Totsp']
X_test = X_test.reshape((X_test.shape[0],1))
In [57]:
Xtr, Xval, Ytr, Yval = train_test_split(X, Y, test_size = 0.1)
model_liv = LinearRegression()
model_liv.fit(Xtr, Ytr)
Out[57]:
In [58]:
pred = model_liv.predict(Xval)
print('rmse:', sqrt(mean_squared_error(Yval, pred)))
In [59]:
Y_test = model_liv.predict(X_test)
ind = 0
for i in range(data.shape[0]):
if (not(data['Livsp'][i] >= 0)):
data['Livsp'][i] = Y_test[ind]
ind += 1
Посмотрим на получившиеся распределения
In [61]:
plt.hist(data['Kitsp'], color = 'r')
plt.show()
In [62]:
plt.hist(data['Livsp'], color = 'r')
plt.show()
Снова удалим маленький процент странных данных
In [63]:
data[data['Kitsp'] < 4].shape
Out[63]:
In [64]:
data = data[data['Kitsp'] >= 4]
data.index = [x for x in range(len(data.index))]
In [65]:
data[data['Livsp'] < 10].shape
Out[65]:
In [66]:
data = data[data['Livsp'] >= 10]
data.index = [x for x in range(len(data.index))]
In [67]:
nans_appearance(data)
Out[67]:
In [70]:
print('min: ', data['Price'].min(), '; max:', data['Price'].max())
In [71]:
plt.hist(data['Price'], color = 'r')
plt.show()
Так данные выглядеть не должны. Сначала уберу outliers, при построении модели попробую поработать с логарифмом от цены.
In [72]:
data[data['Price'] < 1500000].shape[0]
Out[72]:
In [73]:
data.drop(data[data['Price'] < 1500000].index, inplace = True)
data.index = [x for x in range(len(data.index))]
In [74]:
data[data['Price'] > 200000000].shape[0]/data.shape[0]
Out[74]:
In [75]:
data.drop(data[data['Price'] > 200000000].index, inplace = True)
data.index = [x for x in range(len(data.index))]
In [76]:
plt.hist(data['Price'], color = 'r')
plt.show()
Изучим данные по этажам
In [77]:
data[data['Nfloors'] < data['Floor']].shape
Out[77]:
Номер этажа нигде не больше этажности дома, уже неплохо)
In [78]:
plt.hist(data['Floor'], color = 'r')
plt.show()
Не понятно наличие хвоста. Посмотрим на него
In [79]:
tmp = data[data['Floor'] > 40]
tmp[['Floor', 'link']]
Out[79]:
Это реальные данные, но их мало и они сильно портят распределение, уберем их
In [80]:
data.drop(data[data['Floor'] > 40].index, inplace = True)
data.index = [x for x in range(len(data.index))]
In [81]:
plt.hist(data['Floor'], color = 'r')
plt.show()
Так лучше. Посмотрим на распределение Nfloors
In [82]:
plt.hist(data['Nfloors'], color = 'r')
plt.show()
In [83]:
data[data['Nfloors'] > 50].shape[0]/data.shape[0]
Out[83]:
Аналогично
In [84]:
data.drop(data[data['Nfloors'] > 50].index, inplace = True)
data.index = [x for x in range(len(data.index))]
In [85]:
plt.hist(data['Nfloors'], color = 'r')
plt.show()
In [86]:
rooms = data['Rooms'].unique()
rooms.sort()
In [87]:
count_rooms = []
for i in rooms:
tmp = data[data['Rooms'] == i]
count_rooms.append(tmp.shape[0])
In [88]:
width = 1
plot = plt.bar(rooms, count_rooms, width, color='r')
plt.show()
In [89]:
plt.hist(data['Distance'], color='r')
plt.show()
Снова изучим хвост ( > 28 )
In [90]:
data[data['Distance'] > 28].shape[0]
Out[90]:
In [91]:
tmp = data[data['Distance'] > 28]
tmp[['Distance', 'link']]
Out[91]:
In [92]:
data[data['Distance'] > 27].shape[0]/data.shape[0]
Out[92]:
Прикинув по карте, получила 27. Не проходящих порог данных меньше 1%, удаляю их.
In [93]:
data = data[data['Distance'] <= 27]
data.index = [x for x in range(len(data.index))]
Снова посмотрим на распределение
In [94]:
plt.hist(data['Distance'], color='r')
plt.show()
In [95]:
data.columns
Out[95]:
In [96]:
data_corr = data[['Distance', 'Floor', 'Kitsp', 'Livsp', 'New', 'Nfloors', 'Price',
'Rooms', 'Totsp', 'Metrokm']]
In [97]:
correlation = data_corr.corr()
In [98]:
f, ax = plt.subplots(figsize=(10, 10))
cmap = sns.diverging_palette(220, 10, as_cmap=True)
sns.heatmap(correlation, cmap=cmap, vmax=.3,
square=True, xticklabels=correlation.columns.values,
yticklabels=correlation.columns.values,
linewidths=.5, cbar_kws={"shrink": .5}, ax=ax)
Out[98]:
Видна положительная корреляция цены с площадями и числом комнат, а также отрицательная с расстоянием до центра. Выглядит понятно
Посмотрим на зависимость цены от количества комнат
In [99]:
plot = plt.scatter(data['Rooms'], data['Price'], color = 'r')
plt.show()
Чем больше комнат, тем больше цена, многокомнатных меньше, чем других вариантов
Теперь построим аналогичный график для расстояний от центра города
In [100]:
plot = plt.scatter(data['Distance'], data['Price'], color = 'r')
plt.show()
Чем больше расстояние до центра, тем меньше цена
Теперь посмотрим на зависимость цены от общей площади
In [101]:
plot = plt.scatter(data['Totsp'], data['Price'], color = 'r')
plt.show()
Чем больше площадь, тем больше цена
И напоследок посмотрим на зависимость цены от округа
In [102]:
prices_distr = []
for i in districts.keys():
tmp = data[data['Distr'] == districts.get(i)]
price = tmp['Price'].median()
prices_distr.append(price)
In [103]:
plot = plt.bar(range(9), prices_distr, color='r')
plt.show()
In [104]:
districts
Out[104]:
С большим отрывом лидирует ЦАО
In [105]:
to_encode = ['Bal', 'Brick', 'Distr']
data[to_encode] = data[to_encode].fillna(value = 'na')
In [106]:
onehots = pd.get_dummies(data[to_encode])
onehots.head()
data = pd.concat([data, onehots], axis=1)
In [107]:
data.columns
Out[107]:
In [108]:
data.drop(to_encode, axis = 1, inplace = True)
In [109]:
data.drop('link', axis = 1, inplace = True)
In [110]:
data.shape
Out[110]:
In [111]:
data.to_csv('cian_data_clear_for_modeling.csv', index = False)