不動産取引価格予測1位解法

#インポート
import pandas as pd
import numpy as np
import sklearn
import sklearn.metrics
import matplotlib.pyplot as plt
import lightgbm as lgb
# published リネーム準備
pair = [("所在地コード","市区町村コード"),("建蔽率","建ぺい率(%)"),("容積率","容積率(%)"),("駅名","最寄駅:名称"), 
        ("地積","面積(㎡)"),("市区町村名","市区町村名"),('前面道路の幅員','前面道路:幅員(m)'), 
        ("前面道路の方位区分","前面道路:方位"),("前面道路区分","前面道路:種類"),("形状区分","土地の形状"), 
        ("用途区分","都市計画"), ('用途','地域')]
pair_dict = {key:value for key, value in pair}
# データ読み込み
path='./'
test_df = pd.read_csv(path+'test_data.csv')
train_df = pd.read_csv(path+'train_data.csv')
published_df = pd.read_csv(path+'published_land_price.csv',  dtype={'利用の現況': str})
published_df = published_df.rename(columns=pair_dict)
print(test_df.shape, train_df.shape, published_df.shape)
(34844, 27) (356344, 28) (2602, 116)
# train testを結合
train_df['logy'] = np.log1p(train_df['y'])
test_df['y'] = -1
train_df['logy'] = -1
all_df = pd.concat([train_df, test_df])
all_df = all_df.reset_index(drop=True)
print(all_df.shape)
(391188, 29)
all_df.columns
Index(['id', '種類', '地域', '市区町村コード', '都道府県名', '市区町村名', '地区名', '最寄駅:名称',
       '最寄駅:距離(分)', '間取り', '面積(㎡)', '土地の形状', '間口', '延床面積(㎡)', '建築年', '建物の構造',
       '用途', '今後の利用目的', '前面道路:方位', '前面道路:種類', '前面道路:幅員(m)', '都市計画', '建ぺい率(%)',
       '容積率(%)', '取引時点', '改装', '取引の事情等', 'y', 'logy'],
      dtype='object')
#all_df 前処理1
all_df['取引時点'] = all_df['取引時点'].str.replace('年第','.').str.replace('四半期','').str.replace('1','0').str.replace('2','25').str.replace('3','5').str.replace('4','75')
all_df["取引時点"] = pd.to_numeric(all_df["取引時点"], errors='raise')
all_df['面積(㎡)'] = all_df['面積(㎡)'].str.replace('000㎡以上','500')
all_df["面積(㎡)"] = pd.to_numeric(all_df["面積(㎡)"], errors='raise')
all_df.loc[(all_df['種類']=='林地') | (all_df['種類']=='農地'), '面積(㎡)']*=0.1
all_df['最寄駅:距離(分)'] = all_df['最寄駅:距離(分)'].replace('30分?60分','45').replace('1H?1H30','75').replace('1H30?2H','105').replace('2H?','120')
all_df["最寄駅:距離(分)"] = pd.to_numeric(all_df["最寄駅:距離(分)"], errors='raise')
all_df['間口'] = all_df['間口'].str.replace('0.0m以上','5')
all_df["間口"] = pd.to_numeric(all_df["間口"], errors='raise')
# all_df.loc[all_df['間口'].isna(), '間口'] = -(-(all_df.loc[all_df['間口'].isna(), '面積(㎡)']+1)**0.5//5)*5
#all_df 前処理2
all_df['延床面積(㎡)'] = all_df['延床面積(㎡)'].str.replace('㎡以上','').replace('10m^2未満','5')
all_df["延床面積(㎡)"] = pd.to_numeric(all_df["延床面積(㎡)"], errors='raise')
all_df['地域'] = all_df['地域'].str.replace('住宅地','0').str.replace('商業地','005').str.replace('宅地見込地','003').str.replace('工業地','009')
all_df["地域"] = pd.to_numeric(all_df["地域"], errors='raise')
#all_df 前処理3
all_df['最寄駅:名称'] = all_df['最寄駅:名称'].fillna('なし')
all_df['最寄駅:名称'] = all_df['最寄駅:名称'].str.replace('(東京)','').str.replace('(神奈川)','').str.replace('ケ','ヶ')
all_df['最寄駅:名称'] = all_df['最寄駅:名称'].str.replace('(メトロ)','').str.replace('(都電)','').str.replace('(つくばEXP)','')
all_df['最寄駅:名称'] = all_df['最寄駅:名称'].str.replace('(千葉)','').str.replace('(東京メトロ)','').str.strip('()')
#all_df 前処理4
all_df["地区詳細"] = all_df['市区町村名'] + all_df['地区名']
all_df["地区詳細"] = all_df["地区詳細"].str[:5]
all_df['市区町村名'] = all_df['市区町村名'].str.strip('市区町村').str.strip('西多摩郡東京')
#all_df 前処理5
all_df['建築年'] = all_df['建築年'].str.replace('戦前','昭和20年')
all_df['年号'] = all_df['建築年'].str[:2]
all_df['和暦年数'] = pd.to_numeric(all_df['建築年'].str[2:].str.strip('年'), errors='raise')
all_df.loc[all_df['年号']=='昭和','建築年(西暦)'] = all_df['和暦年数'] + 1925
all_df.loc[all_df['年号']=='平成','建築年(西暦)'] = all_df['和暦年数'] + 1988
#all_df 前処理6
all_df['間取り'] = all_df['間取り'].str.replace('オープンフロア','0、O').str.replace('スタジオ','0、T').str.replace('メゾネット','0、M')
all_df['間取り'] = all_df['間取り'].str.replace('L','、L').str.replace('D','、D').str.replace('K','、K').str.replace('S','、S').str.replace('R','、R')
all_df['間取り'] = all_df['間取り'].str.replace('1','1').str.replace('2','2').str.replace('3','3').str.replace('+','')
all_df['間取り'] = all_df['間取り'].str.replace('4','4').str.replace('5','5').str.replace('6','6').str.replace('7','7')

publish

#published_df 前処理
published_df["面積(㎡)"] = published_df["面積(㎡)"].clip(0, 3000)
['その他',  '住宅', '店舗', '事務所', '作業場', '倉庫', '共同住宅', '工場', '駐車場']
[         '住宅', '店舗', '事務所', '銀行', '旅館', '給油所', '工場', '倉庫', '農地', '山林', '医院', '空地', '作業場', '原野', 'その他', '用材', '雑木']
riyo_list = np.array(['住宅', '店舗', '事務所', '_', '_', '_', '工場', '倉庫', '_', '_', '_', '_', '作業場', '_', 'その他', '_', '_'])
riyo_list
riyo_now = [[0]*(17 - len(num))+list(map(int, list(num))) for num in published_df['利用の現況'].values]
riyo_now = np.array(riyo_now)
riyo_lists = ['、'.join(riyo_list[onehot.astype('bool')]) for onehot in riyo_now]
for i in range(len(riyo_lists)):
    if 'その他' in riyo_lists[i]:
        riyo_lists[i] = riyo_lists[i].replace('その他', published_df.loc[i, '利用状況表示'])
    riyo_lists[i] = riyo_lists[i].replace('_', 'その他').replace('、雑木林', '').replace('、診療所', '').replace('、車庫', '').replace('、集会場', '')\
    .replace('、寄宿舎', '').replace('、駅舎', '').replace('、劇場', '').replace('、物置', '').replace('、集会場', '').replace('、映画館', '')\
    .replace('、遊技場', '').replace('兼', '、').replace('、建築中', 'その他').replace('、試写室', '').replace('、寮', '').replace('、保育所', '')\
    .replace('、治療院', '').replace('、診療所', '').replace('、荷捌所', '').replace('建築中', 'その他').replace('事業所', '事務所').replace('、営業所', '')
published_df['利用の現況'] = riyo_lists
#最信念を取り出してpublished_df2を作成
kakakus = []
for i, year in enumerate(published_df.columns[-37:-38:-1]):
    df = pd.merge(published_df[published_df.columns[:-75:]], published_df[published_df[year]>0][['id', year]], on='id')
    df['取引時点'] = 2019-i
    df = df.rename(columns={year: 'y'})
    df['y'] = df['y'] /100000
    kakakus.append(df)
published_df2 = pd.concat(kakakus)
published_df2.shape
(2602, 43)
#published_df前処理1
published_df2['最寄駅:距離(分)'] = published_df2['駅距離']//50
published_df2.loc[:, '最寄駅:距離(分)'][published_df2['最寄駅:距離(分)']>120] = 120
published_df2['間口(比率)'] = published_df2['間口(比率)'].clip(10, 100)
published_df2['奥行(比率)'] = published_df2['奥行(比率)'].clip(10, 100)
published_df2['間口'] = np.sqrt(published_df2['面積(㎡)']/published_df2['間口(比率)']/published_df2['奥行(比率)'])*published_df2['間口(比率)']
C:\Users\we_lo\AppData\Local\Continuum\anaconda3\envs\gpu\lib\site-packages\ipykernel_launcher.py:3: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  This is separate from the ipykernel package so we can avoid doing imports until
#published_df前処理2
published_df2['種類'] = np.nan
published_df2['建築年(西暦)'] = np.nan
published_df2['建物の構造'] = published_df2['建物構造'].str.replace('SRC','SRC').str.replace('RC','RC').str.replace('W','木造').str.replace('LS','軽量鉄骨造').str.replace('S','鉄骨造')
published_df2['建物の構造'] = published_df2['建物の構造'].str.replace('[0-9]','').str.replace('FB','').str.replace('B','_').value_counts()
published_df2['最寄駅:名称'] = published_df2['最寄駅:名称'].str.replace('ケ','ヶ')
#published_df前処理3
se = published_df2['住居表示'].str.strip('東京都').str.replace('大字', '').str.replace('字', '')
for num in ['1', '2', '3', '4', '5', '6', '7', '8', '9']:
    se = se.str.split(num).str[0].str.strip()
published_df2['地区詳細'] = se
published_df2['地区詳細'] = published_df2['地区詳細'].str[:5]
published_df2['市区町村名'] = published_df2['市区町村名'].str.strip('市区町村').str.strip('西多摩郡東京')
#published_df前処理4
rep ={'1低専':'第1種低層住居専用地域', '2低専':'第2種低層住居専用地域', '1中専':'第1種中高層住居専用地域', '2中専':'第2種中高層住居専用地域', '1住居':'第1種住居地域',
 '2住居':'第2種住居地域', '準住居':'準住居地域', '商業':'商業地域', '近商':'近隣商業地域', '工業':'工業地域', '工専':'工業専用地域',  '準工':'準工業地域', '田園住':'田園住居地域'}
for key, value in rep.items():
    published_df2.loc[:, '都市計画'] = published_df2.loc[:, '都市計画'].str.replace(key,value)
#published_df前処理5
published_df2['logy'] = np.log1p(published_df2['y'])
published_df2 = published_df2.rename(columns={'利用の現況': '用途'})
published_df2.columns
Index(['id', '経度', '緯度', '市区町村コード', '地域', '連番', '年次', '前年所在地コード', '前年用途',
       '前年連番', '市区町村名', '住居表示', '行政', '面積(㎡)', '用途', '利用状況表示', '建物構造', '施設',
       '土地の形状', '間口(比率)', '奥行(比率)', '階層(地上)', '階層(地下)', '前面道路:種類', '前面道路:方位',
       '前面道路:幅員(m)', '前面道路の駅前区分', '前面道路の舗装状況', '側道区分', '側道方位区分', '交通施設との近接区分',
       '周辺の土地の利用の現況', '最寄駅:名称', '駅距離', '都市計画', '防火区分', '都市計画区分', '森林区分',
       '公園区分', '建ぺい率(%)', '容積率(%)', 'y', '取引時点', '最寄駅:距離(分)', '間口', '種類',
       '建築年(西暦)', '建物の構造', '地区詳細', 'logy'],
      dtype='object')
'''
id 経度 緯度 ○所在地コード 年次 ○市区町村名 住居表示 ◎地積 ◎利用の現況&利用状況表示 ◎建物構造 ◎形状区分 ◎間口(比率) ◎奥行(比率)階層(地上 階層(地下)◎前面道路区分 ◎前面道路の方位区分
◎前面道路の幅員  ◎駅名 ◎駅距離 ◎用途区分  ◎建蔽率 ◎容積率 価格 ◎用途

 連番 行政 施設 前面道路の駅前区分 前面道路の舗装状況 側道区分 側道方位区分 交通施設との近接区分 防火区分  森林区分 公園区分 共通地点区分 選定年次ビット 周辺の土地の利用の現況 都市計画区分

○種類 ◎地域 ○市区町村コード◎最寄駅:距離(分)◎面積(㎡)◎建ぺい率(%) ◎容積率(%)◎間口 取引時点◎前面道路:方位 
◎前面道路:種類 ◎前面道路:幅員(m)◎建物の構造○市区町村名 地区名 ◎最寄駅:名称◎用途

id     ◎土地の形状  延床面積(㎡) 建築年  
今後の利用目的 ◎都市計画   ○改装  y

都道府県名 間取り 取引の事情等

'''
'\nid 経度 緯度 ○所在地コード 年次 ○市区町村名 住居表示 ◎地積 ◎利用の現況&利用状況表示 ◎建物構造 ◎形状区分 ◎間口(比率) ◎奥行(比率)階層(地上 階層(地下)◎前面道路区分 ◎前面道路の方位区分\n◎前面道路の幅員\u3000 ◎駅名 ◎駅距離 ◎用途区分  ◎建蔽率 ◎容積率 価格\u3000◎用途\n\n 連番 行政 施設 前面道路の駅前区分 前面道路の舗装状況 側道区分 側道方位区分 交通施設との近接区分 防火区分  森林区分 公園区分 共通地点区分 選定年次ビット 周辺の土地の利用の現況\u3000都市計画区分\n\n○種類 ◎地域 ○市区町村コード◎最寄駅:距離(分)◎面積(㎡)◎建ぺい率(%) ◎容積率(%)◎間口 取引時点◎前面道路:方位 \n◎前面道路:種類 ◎前面道路:幅員(m)◎建物の構造○市区町村名 地区名 ◎最寄駅:名称◎用途\n\nid     ◎土地の形状  延床面積(㎡) 建築年  \n今後の利用目的 ◎都市計画   ○改装  y\n\n都道府県名\u3000間取り 取引の事情等\n\n'
# 使う列を指定
# 
target_columns = ['地域', '種類', '市区町村コード', '面積(㎡)','建ぺい率(%)', '容積率(%)','最寄駅:距離(分)', '前面道路:方位', '地区詳細','土地の形状','市区町村名',
                  '前面道路:種類', '前面道路:幅員(m)',  '間口', '建物の構造', '最寄駅:名称',  '用途', '都市計画', '建築年(西暦)', 'y', 'logy']
#平均値化取得用
tika = published_df2[target_columns]
tika.head()
地域 種類 市区町村コード 面積(㎡) 建ぺい率(%) 容積率(%) 最寄駅:距離(分) 前面道路:方位 地区詳細 土地の形状 ... 前面道路:種類 前面道路:幅員(m) 間口 建物の構造 最寄駅:名称 用途 都市計画 建築年(西暦) y logy
0 0 NaN 13364 224 70 200 9 神津島村 _ ... 村道 22 13.662601 NaN キャンドゥ前 住宅 _ NaN 0.076 0.073250
1 5 NaN 13364 203 70 200 11 西 神津島村 _ ... 村道 35 17.449928 NaN キャンドゥ前 住宅、店舗 _ NaN 0.095 0.090754
2 0 NaN 13364 239 70 200 26 南東 神津島村 _ ... 村道 40 16.935171 NaN 神津島港 住宅 _ NaN 0.065 0.062975
3 0 NaN 13228 309 40 80 56 北西 あきる野市 _ ... 市道 47 21.529050 NaN 武蔵五日市 住宅 _ NaN 0.370 0.314811
4 0 NaN 13205 135 40 80 12 青梅市沢井 _ ... 私道 45 9.486833 NaN 軍畑 住宅 第1種低層住居専用地域 NaN 0.440 0.364643

5 rows × 21 columns

#試しに見てみる
col ='最寄駅:名称'
print('only all_df', set(all_df[col].unique()) - set(published_df2[col].unique()))
print('only publish_df', set(published_df2[col].unique()) - set(all_df[col].unique()))
tdf = tika[[col, 'y']].groupby([col]).mean().sort_values('y', ascending=False)
tdf[tdf['y']>50]
only all_df {'', '鬼子母神前', '飛鳥山', '学習院下', '西ヶ原四丁目', '浅草(東武・都営・', '所沢', '品川シーサイド', '潮見', '尾久', '栗平', '東池袋四丁目', '国立競技場', '東所沢', '有明', '幡ヶ谷', '立川南', '山下', '新宿西口', '南鳩ヶ谷', '新木場', 'テレポート', '舞浜', '牛田', '飯能', '町屋二丁目', '御嶽山', '赤土小学校前', '竹芝', '高尾山口', '古里', '東あずま', '新庚申塚', '新座', '松が谷', '芝浦ふ頭', '荒川二丁目', '宮ノ前', '黒川', '滝野川一丁目', '面影橋', '万願寺', '府中本町', '代々木公園', '日の出', '遊園地西', '矢部', '荒川一中前', '小田急多摩センター', '荒川区役所前', '川口元郷', '桜ヶ丘', '南新宿', '戸越公園', '馬喰横山', '六郷土手', '鳩ノ巣', '荒川七丁目', '武蔵小杉', '高野', '沢井', '牛込柳町', '中央大学・明星大学', '相模原', '東向島', '国会議事堂前', '石神前', 'お台場海浜公園', '奥多摩', '浦安', '向原', '青海', '栄町', '白丸', '天空橋', '有明テニスの森', '亀戸水神', '曳舟', '京成金町', '新豊洲', '都庁前', '高松', '御嶽', '京成関屋', '多摩センター', '巣鴨新田', '立飛', '川井', '乃木坂', '長津田', '雑司ヶ谷', '京成上野', '町屋駅前', 'なし', '西武新宿', '馬喰町', '三ノ輪橋', '小田急永山', '庚申塚', '西日暮里', '上野御徒町', '昭和島'}
only publish_df {'町役場前停', '都営豊島園', '都営市ヶ谷', '青灯台入口', '霞ヶ関', '三根出張所前停', '焼場停', '村役場前', '下地停', 'キャンドゥ前', '裁判所前停', '元町港', '神津島港', '東京', '新島港', 'つくばエクスプレス浅草', '小田急町田', '錆ヶ浜停', '都営水道橋', '中之郷出張所前停', '奥村', '錆ヶ浜港入口停'}
y
最寄駅:名称
銀座 376.500000
東京 263.800000
銀座一丁目 241.600000
明治神宮前 240.000000
大手町 198.100000
京橋 170.000000
日比谷 145.733333
原宿 136.000000
霞ヶ関 130.000000
有楽町 128.000000
新宿 110.063636
新宿三丁目 108.500000
日本橋 105.375000
内幸町 93.775000
新橋 80.900000
渋谷 74.270588
虎ノ門 72.975000
宝町 72.700000
竹橋 72.300000
汐留 69.000000
東銀座 59.800000
外苑前 56.400000
青山一丁目 55.066667
淡路町 54.900000
上野広小路 53.100000
赤坂見附 50.933333
# all_df を抽出
all_df_target = all_df[target_columns+['延床面積(㎡)', '改装', '間取り', '取引の事情等']]
all_df_target = all_df_target.reset_index(drop=True)
#部屋数などを取得
all_df_target['間取り数'] = pd.to_numeric(all_df_target['間取り'].str[0], errors='raise')
all_df_target['部屋数'] = all_df_target['間取り'].str[2:].str.len()//2+1+all_df_target['間取り数']
#published_df2の集計値を結合 面積と掛け算、最寄り駅距離で補正
cols = ['地域', '市区町村コード', '地区詳細', '建ぺい率(%)', '容積率(%)', '都市計画', '前面道路:方位', '前面道路:種類', '最寄駅:名称']
for col in cols:
    print(col, all_df_target.shape)
    all_df_target = pd.merge(all_df_target, tika[[col, 'y']].groupby(col).mean().rename(columns={'y':col+'y'}), on=col, how='left')

all_df_target.loc[all_df_target['地区詳細y'].isna(), '地区詳細y'] = all_df_target.loc[all_df_target['地区詳細y'].isna(), '市区町村コードy']
all_df_target.loc[all_df_target['最寄駅:名称y'].isna(), '最寄駅:名称y'] = all_df_target.loc[all_df_target['最寄駅:名称y'].isna(), '地区詳細y']
    
cols = ['市区町村コード', '地区詳細', '最寄駅:名称']
#cols = ['地域', '市区町村コード', '地区詳細', '建ぺい率(%)', '容積率(%)', '都市計画', '前面道路:方位', '前面道路:種類', '最寄駅:名称']
for col in cols:
    all_df_target['m2x'+col] = all_df_target[col+'y'] * all_df_target['面積(㎡)']/100
    #all_df_target['nm2x'+col] = all_df_target[col+'y'] * all_df_target['延床面積(㎡)']/100
    all_df_target['m2m2x'+col] = all_df_target[col+'y'] * (all_df_target['面積(㎡)']+all_df_target['延床面積(㎡)'].fillna(0))/100
    all_df_target['m2m2x'+col+'_sta'] = all_df_target['m2m2x'+col] * (1 - all_df_target['最寄駅:距離(分)'].clip(0, 10)*0.02)
print(all_df_target.shape)
地域 (391188, 27)
市区町村コード (391188, 28)
地区詳細 (391188, 29)
建ぺい率(%) (391188, 30)
容積率(%) (391188, 31)
都市計画 (391188, 32)
前面道路:方位 (391188, 33)
前面道路:種類 (391188, 34)
最寄駅:名称 (391188, 35)
(391188, 45)
all_df_target
地域 種類 市区町村コード 面積(㎡) 建ぺい率(%) 容積率(%) 最寄駅:距離(分) 前面道路:方位 地区詳細 土地の形状 ... 最寄駅:名称y m2x市区町村コード m2m2x市区町村コード m2m2x市区町村コード_sta m2x地区詳細 m2m2x地区詳細 m2m2x地区詳細_sta m2x最寄駅:名称 m2m2x最寄駅:名称 m2m2x最寄駅:名称_sta
0 NaN 中古マンション等 13101 55.0 80.0 600.0 1.0 NaN 千代田区飯 NaN ... 23.325 34.818967 34.818967 34.122588 20.7350 20.7350 20.3203 12.82875 12.82875 12.572175
1 NaN 中古マンション等 13101 20.0 80.0 500.0 5.0 NaN 千代田区飯 NaN ... 23.325 12.661443 12.661443 11.395298 7.5400 7.5400 6.7860 4.66500 4.66500 4.198500
2 NaN 中古マンション等 13101 45.0 80.0 500.0 3.0 NaN 千代田区飯 NaN ... 23.325 28.488246 28.488246 26.778951 16.9650 16.9650 15.9471 10.49625 10.49625 9.866475
3 NaN 中古マンション等 13101 20.0 80.0 500.0 5.0 NaN 千代田区飯 NaN ... 23.325 12.661443 12.661443 11.395298 7.5400 7.5400 6.7860 4.66500 4.66500 4.198500
4 5.0 宅地(土地と建物) 13101 80.0 80.0 500.0 3.0 南西 千代田区飯 ほぼ台形 ... 23.325 50.645770 259.559574 243.985999 30.1600 154.5700 145.2958 18.66000 95.63250 89.894550
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
391183 0.0 宅地(土地と建物) 13401 840.0 60.0 200.0 NaN 北西 八丈町三根 ほぼ台形 ... 0.311 1.380400 1.479000 NaN 2.6124 2.7990 NaN 2.61240 2.79900 NaN
391184 0.0 宅地(土地) 13401 370.0 70.0 200.0 NaN 八丈町三根 ほぼ長方形 ... 0.311 0.608033 0.608033 NaN 1.1507 1.1507 NaN 1.15070 1.15070 NaN
391185 0.0 宅地(土地) 13401 520.0 70.0 200.0 NaN 北東 八丈町三根 不整形 ... 0.311 0.854533 0.854533 NaN 1.6172 1.6172 NaN 1.61720 1.61720 NaN
391186 0.0 宅地(土地) 13421 380.0 40.0 200.0 NaN 小笠原村父 ほぼ台形 ... 0.522 1.983600 1.983600 NaN 1.9836 1.9836 NaN 1.98360 1.98360 NaN
391187 0.0 宅地(土地) 13421 350.0 70.0 200.0 NaN 北東 小笠原村母 不整形 ... 0.522 1.827000 1.827000 NaN 1.8270 1.8270 NaN 1.82700 1.82700 NaN

391188 rows × 45 columns

#、で区切られてるのをonehot化
all_df_target = all_df_target.join(all_df_target['建物の構造'].str.get_dummies(sep='、'))
all_df_target = all_df_target.join(all_df_target['用途'].str.get_dummies(sep='、'))
all_df_target = all_df_target.join(all_df_target['間取り'].str[2:].str.get_dummies(sep='、'))
# all_df_target = all_df_target.drop(columns=['建物の構造', '用途', '間取り'])
all_df_target['m2kai'] = all_df_target['延床面積(㎡)'] / all_df_target['面積(㎡)']
all_df_target['men_magu'] = all_df_target['面積(㎡)'] / all_df_target['間口']
all_df_target['men_toshi'] = all_df_target['面積(㎡)'] * all_df_target['都市計画y']
all_df_target['douro_haba_syu'] = all_df_target['前面道路:種類y'] * all_df_target['前面道路:幅員(m)']
all_df_target['toshi_tyoson'] = all_df_target['都市計画y'] * all_df_target['市区町村コードy']
all_df_target['syuru_magu'] = all_df_target['間口'] / all_df_target['前面道路:種類y'] 
all_df_target['haba_magu'] = all_df_target['間口'] / all_df_target['前面道路:幅員(m)']
all_df_target['heya_m'] = all_df_target['面積(㎡)'] / all_df_target['部屋数'] 
#確認
all_des = all_df_target.describe(include='all')
all_des.round(2)
地域 種類 市区町村コード 面積(㎡) 建ぺい率(%) 容積率(%) 最寄駅:距離(分) 前面道路:方位 地区詳細 土地の形状 ... S T m2kai men_magu men_toshi douro_haba_syu toshi_tyoson syuru_magu haba_magu heya_m
count 214339.00 391188 391188.00 391188.00 385417.00 385417.00 380898.00 213915 390942 213940 ... 391188.00 391188.00 131541.00 196158.00 386008.00 209169.00 386008.00 193302.00 193651.00 170427.00
unique NaN 5 NaN NaN NaN NaN NaN 9 1236 9 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
top NaN 中古マンション等 NaN NaN NaN NaN NaN 大田区大森 ほぼ長方形 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
freq NaN 176330 NaN NaN NaN NaN NaN 33859 3345 74779 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
mean 0.58 NaN 13139.98 116.96 62.08 264.86 10.72 NaN NaN NaN ... 0.00 0.00 1.23 17.48 880.97 67.51 189.86 2.29 1.87 11.78
std 1.67 NaN 46.23 200.10 13.13 159.58 9.77 NaN NaN NaN ... 0.07 0.01 1.00 12.76 2132.03 164.01 500.68 4.82 1.46 14.83
min 0.00 NaN 13101.00 10.00 30.00 50.00 0.00 NaN NaN NaN ... 0.00 0.00 0.02 0.52 28.97 0.16 1.02 0.02 0.03 1.67
25% 0.00 NaN 13110.00 50.00 60.00 150.00 5.00 NaN NaN NaN ... 0.00 0.00 0.78 10.62 284.03 11.79 16.20 0.67 1.00 10.00
50% 0.00 NaN 13117.00 70.00 60.00 200.00 8.00 NaN NaN NaN ... 0.00 0.00 1.00 13.75 400.92 25.04 26.08 1.36 1.60 10.83
75% 0.00 NaN 13201.00 115.00 80.00 300.00 13.00 NaN NaN NaN ... 0.00 0.00 1.40 18.75 719.79 64.15 79.26 2.95 2.29 12.50
max 9.00 NaN 13421.00 2500.00 80.00 1300.00 120.00 NaN NaN NaN ... 1.00 1.00 66.00 625.00 89973.68 4082.99 2958.25 338.46 45.83 2500.00

11 rows × 76 columns

#ラベルエンコード
for col in all_df_target.columns:
    if all_df_target[col].dtype == 'object':
        print(col, all_df_target[col].dtype)
        #temp = all_df_target[[col, 'logy']].groupby(col).mean()['logy']
        #all_df_target[col] = all_df_target[col].map(temp)
        labels, uniques = all_df_target[col].factorize(sort=True)
        all_df_target[col] = labels
種類 object
前面道路:方位 object
地区詳細 object
土地の形状 object
市区町村名 object
前面道路:種類 object
建物の構造 object
最寄駅:名称 object
用途 object
都市計画 object
改装 object
間取り object
取引の事情等 object
all_df_target.columns
Index(['地域', '種類', '市区町村コード', '面積(㎡)', '建ぺい率(%)', '容積率(%)', '最寄駅:距離(分)',
       '前面道路:方位', '地区詳細', '土地の形状', '市区町村名', '前面道路:種類', '前面道路:幅員(m)', '間口',
       '建物の構造', '最寄駅:名称', '用途', '都市計画', '建築年(西暦)', 'y', 'logy', '延床面積(㎡)',
       '改装', '間取り', '取引の事情等', '間取り数', '部屋数', '地域y', '市区町村コードy', '地区詳細y',
       '建ぺい率(%)y', '容積率(%)y', '都市計画y', '前面道路:方位y', '前面道路:種類y', '最寄駅:名称y',
       'm2x市区町村コード', 'm2m2x市区町村コード', 'm2m2x市区町村コード_sta', 'm2x地区詳細',
       'm2m2x地区詳細', 'm2m2x地区詳細_sta', 'm2x最寄駅:名称', 'm2m2x最寄駅:名称',
       'm2m2x最寄駅:名称_sta', 'ブロック造', '木造', '軽量鉄骨造', '鉄骨造', 'RC', 'SRC', 'その他',
       '事務所', '住宅', '作業場', '倉庫', '共同住宅', '工場', '店舗', '駐車場', 'D', 'K', 'L', 'M',
       'O', 'R', 'S', 'T', 'm2kai', 'men_magu', 'men_toshi', 'douro_haba_syu',
       'toshi_tyoson', 'syuru_magu', 'haba_magu', 'heya_m'],
      dtype='object')
#トレーニングデータ取り出し
x_train = all_df_target[all_df_target['y']>=0].drop(['y', 'logy'], axis=1).values
y_train = all_df_target[all_df_target['y']>=0].loc[:, 'y'].values
print(x_train.shape, y_train.shape)
(356344, 74) (356344,)
# テストデータ取り出し
x_test = all_df_target[all_df_target['y']<0].drop(['y', 'logy'], axis=1).values
y_test = -all_df_target[all_df_target['y']<0].loc[:, 'y'].values
print(x_test.shape, y_test.shape)
(34844, 74) (34844,)
#外れ値と予想
large_y_index = [1305, 1960, 8069]

推論

#5foldでモデルを作成、評価
from sklearn.model_selection import KFold
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.metrics import mean_squared_error
import lightgbm as lgb

kf = KFold(n_splits=5, shuffle=True, random_state=1)
s = list(kf.split(x_train, y_train))
#yに補正をかけて全体で推論
ymin = 0.1
ymax = 30000
n = 5
line = 7000
y_train2 = y_train.copy()
y_train2[y_train2>=line] = (y_train2[y_train2>=line]-line)/n+line
print(y_train2.max())

gbk = lgb.LGBMRegressor(n_estimators=500, max_depth=9, subsample=0.8, colsample_bytree=0.8, min_child_weight=1)
gbk.fit(x_train, np.clip(y_train2, ymin, ymax))
y_pred = np.clip(gbk.predict(x_test), 0, 100000)
y_val = y_test
acc_gbk = np.sqrt(mean_squared_error(y_val, np.where(y_val>10000, y_val, y_pred))).round(1)
acc_gbk2 = np.sqrt(mean_squared_error(y_val, np.where(y_val>5000, y_val, y_pred))).round(1)
print(n, line, 'rmse', acc_gbk, 'rmse-1000', acc_gbk2)
17800.0
5 7000 rmse 159.9 rmse-1000 159.9
#yに補正をかけて全体で推論
ymin = 0.1
ymax = 30000
n = 5
line = 7000
y_train2 = y_train.copy()
y_train2[y_train2>=line] = (y_train2[y_train2>=line]-line)/n+line
print(y_train2.max())

gbk = lgb.LGBMRegressor(n_estimators=300, max_depth=6, subsample=0.8, colsample_bytree=0.8, min_child_weight=1)
gbk.fit(x_train, np.clip(y_train2, ymin, ymax))
y_pred2 = np.clip(gbk.predict(x_test), 0, 100000)
y_val = y_test
    
acc_gbk = np.sqrt(mean_squared_error(y_val, np.where(y_val>10000, y_val, y_pred2))).round(1)
acc_gbk2 = np.sqrt(mean_squared_error(y_val, np.where(y_val>5000, y_val, y_pred2))).round(1)
print(n, line, 'rmse', acc_gbk, 'rmse-1000', acc_gbk2)
17800.0
5 7000 rmse 162.7 rmse-1000 162.7
#大きいもののみを使用して推論
gbk_l = lgb.LGBMRegressor(n_estimators=200, max_depth=4, subsample=0.8, colsample_bytree=0.8, min_child_weight=1)
gbk_l.fit(x_train[y_train>5000], y_train[y_train>5000])
y_pred_l = np.clip(gbk_l.predict(x_test), 0, 100000).round(1)
acc_gbk = np.sqrt(mean_squared_error(y_pred_l[large_y_index], y_test[large_y_index])).round(1)
#おみくじ
import itertools
a = [0.5, 1, 1.5, 2]
b = [0.5, 1, 1.5, 2]
c = [1, 2, 3]
for use in range(48):
    kumi = list(itertools.product(a,b,c))
    #use = 0
    use_kumi =np.array(kumi[use])
    use_kumi_str = '_'.join(list(map(str, kumi[use])))
    print(use_kumi, use_kumi_str)
    kesu=np.array([[0.25, 0.95]]).reshape(2,1)
    preds = (np.array([y_pred, y_pred2])*kesu).sum(0)
    preds[large_y_index] = y_pred_l[large_y_index]*use_kumi
    test_df['y'] = preds.round(1)
    test_df[['id', 'y']].head()
    test_df[['id', 'y']].to_csv('20200418_last_{}_{}.csv'.format(use, use_kumi_str), index=False)
[0.5 0.5 1. ] 0.5_0.5_1

添付データ

  • 20200419_last.ipynb?X-Amz-Expires=10800&X-Amz-Date=20240420T065127Z&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIP7GCBGMWPMZ42PQ
  • Aws4 request&x amz signedheaders=host&x amz signature=b5c8be423932af730174a14ecc7a9e8d94f71de3421a5c29cd56618e00d846c0
    chun1182

    お疲れさまでした。 shakeが起きると予想して乗りこなすような処理を入れました。 データの処理・モデル作成というよりこういう評価指標を使うとこのようなことが起きるということが学べるかなと思います。

    一番大事なのは、「外れ値を含むデータを評価するのにRMSEを使用するのは良くない」ということです。 理由は「RMSEについてと、スコアを上げるための考え方」というトピックを参考にしていただけたらと思います。 これによってスコアは少数の非常に大きい値に大きく左右されると考えられました。

    あとは何個出してもいいということだったので、出力の特に大きいと思われる3つ(10,000を超える可能性があるのはこの3つだけな感じだった)について、適当な係数をかけて提出しました。 id:価格でいうと{1306:30000, 1961:13000, 8070:28000}くらいなんだと思います。試してみてください。

    特徴量作ったりしましたがコンペでスコアを上げるという目的からするとあまり意味なかった気もします。 cvとかも途中作ってたのですが大きい値をできるだけ学習データに入れたかったので最後消しました。

    Aws4 request&x amz signedheaders=host&x amz signature=758bd5892dfb90c7c5676f045481d9fd325ed257d1990e919e5bae45a60554c3
    masato8823

    お疲れさまでした! トピックや解法等参考になりました。

    質問なんですが、In[36]の 外れ値と予測したデータのインデックスと、In[38]の ymax の値は、どのような過程を経て決定されたのでしょうか。また、どこかにそれの根拠や決定過程がわかるコードがあれば、恐縮ですが教えてほしいです。

    Aws4 request&x amz signedheaders=host&x amz signature=b5c8be423932af730174a14ecc7a9e8d94f71de3421a5c29cd56618e00d846c0
    chun1182

    質問ありがとうございます! 非常に申し訳ないですが、ハイパーパラメータの根拠となるコードはこのコードに塗りつぶされて残ってないです・・・。そこの勝負にならないだろうと思ってたのでグリッドサーチとか詰める作業をしなかったもので。

    ymaxですが基本的には「RMSEについてと、スコアを上げるための考え方」のトピックのような形でCVを作ったり、publicLBの値から決めました。 初めはyの値を10,000にclipしたらpublic上がったのでそうしてましたが、均一にclipするより傾きつけてclipしたほうがいいかなと思って、大きいyが大きくなりすぎないように一定値を超えた分を割ることにしました。一定値と割る値はCVで選んでます。念入りには調べてないですが。

    外れ値と予想したインデックスはCVそれぞれでtestを推論させてみたところこの3つ以外はどれも5,000以下で、10,000を超えるものはこの3つしか無いんじゃないかと思って選びました。指標的に10,000を超えるような誤差はダメージが大きいので、この3つだけ係数の対処をしようと決めました。 あとは3つとも面積が大きいのも要因です。面積・延床面積は2000m^2以上までしかわからないので、もしかしたら10,000m^2とかそれ以上という可能性もありますし。

    Aws4 request&x amz signedheaders=host&x amz signature=758bd5892dfb90c7c5676f045481d9fd325ed257d1990e919e5bae45a60554c3
    masato8823

    回答ありがとうございます! ymaxについてはおおむね理解できました。ありがとうございます。

    追加で、外れ値と予想したインデックスについて質問があります。 In[38]、In[39]どちらも実行してみて、それぞれの y_pred, y_pred2 を見てみたんですが、値が大きい順のインデックスがどちらとも[1305, 1960, 13653, 303, ...,]の順に並んでいて、[1305, 1960, 8069]の3つが大きなる結果にはなっていなかったです。ここのモデルとは別のモデルで、例の3つのインデックスが大きいと判断されたということですか?それとも別の方法でしょうか。 もしかしたら私の実行ミスかもしれません、もし私の実行ミスなら申し訳ないです・・・

    Aws4 request&x amz signedheaders=host&x amz signature=b5c8be423932af730174a14ecc7a9e8d94f71de3421a5c29cd56618e00d846c0
    chun1182

    そうですね。3つを選んだのはymax入れる前です。なのでn=1で動かすと3番目に大きくなると思います。 8069は3つの中でも特に予測が不安定で、どれかの特徴量を変えると10,000になったり8000になったりします。 それで初めは8069はせいぜい15,000くらいかなと思って、c = [1, 2]だけでいいかなと思ってたんですが、他の2つを4種類ふってるのにこれだけ2種類は寂しいなと思って3種類にしたらそれが良かったみたいです。

    Icon11
    lingmu3

    皆様お疲れ様でした。私は初めてのコンペ参加で良い勉強になりました。コンペという側面もあると思いますが、「おみくじ」のようなやり方もあるとは驚きました。

    Aws4 request&x amz signedheaders=host&x amz signature=b5c8be423932af730174a14ecc7a9e8d94f71de3421a5c29cd56618e00d846c0
    chun1182

    ここまで極端なやり方はあまり参考にはならないと思いますが、privateにどんなデータが来るかなあと言うのは考えるといいことがあるかもです。 特に重要そうなデータを選んでそこだけいじって提出して、スコアの動きを見ることでpuclic,privateどちらにいるのかとかを読んだりできます。

    Icon14
    yukke

    ソリューション公開ありがとうございます。とても参考になりました。 おみくじ前に[0.25, 0.95]という重みで別パラメータモデルの出力をアンサンブルしていると思うのですが、 この値を設定したのには理由はありますか? 足して1を超えるのが意外だなと思いまして。よろしくお願いいたします。

    Aws4 request&x amz signedheaders=host&x amz signature=b5c8be423932af730174a14ecc7a9e8d94f71de3421a5c29cd56618e00d846c0
    chun1182

    自分もこれはCVでそうなって意外だったのですが、この方がスコア良かったです。 概念的にはRMSEはどうせ外すなら基本上に外したほうが大失敗が抑えられて得という感じでしょうか。 別サイトですがsignateさんの「土地の販売価格の推定」というコンペでは評価値がMAPEなのですが、これは予測値を小さくするような係数をかけることで精度が上がるというのが入賞者レポートにありました。

    Icon0
    Akky

    お疲れ様です。

    今回初めて一人でコンペに参加した初学者ですので、素人質問で申し訳ありませんがお聞きしたいことがあります。 前処理4において行われている

    all_df["地区詳細"] = all_df['市区町村名'] + all_df['地区名']

    all_df["地区詳細"] = all_df["地区詳細"].str[:5]

    all_df['市区町村名'] = all_df['市区町村名'].str.strip('市区町村').str.strip('西多摩郡東京')

    の意図はどういったものなのでしょうか? 地区詳細を5文字で切っているのは、データ削減のためでしょうか?

    Aws4 request&x amz signedheaders=host&x amz signature=b5c8be423932af730174a14ecc7a9e8d94f71de3421a5c29cd56618e00d846c0
    chun1182

    これはpublishのデータとtrainの市区町村名の詳細を合わせようとしていて ~市の後のを合わせようとしたのですが、 地区名の処理が意外と面倒で(n番地とかn丁目とか)等結構面倒で、「市区町村名+地区名が5文字あればある程度地区わかれるだろ」と雑に考えたところです。 なのでpublishとtest+trainが一致しやすくするためっと言ったところです。

    Icon0
    Akky

    なるほど!丁寧にお答えいただきありがとうございました!

    Favicon
    new user
    コメントするには 新規登録 もしくは ログイン が必要です。