YOLO813

机器学习(8)- 房产价格增幅预测的综合训练

    以下数据源来自于互联网公开的北京二手房成交数据,仅作个人学习使用。

    目的:通过对2015年前三季度数据的分析,完成对房价涨幅超过10%的小区进行预判,并利用第4个季度的数据来验证模型的准确率。这是一个能与否的问题,所以是一个二分类问题,考虑使用逻辑回归来解决。

    示例数据:

data.head()


    提取需要的特征:

data= data[["cjzongjia","cjdanjia","cjxiaoqu","cjlouceng","cjshijian","bankuai","mendian","xingming","congyenianxian"]]


    先将数据改成时间序列,方便取得2015年的数据,有三种思路,一种是利用str的contains函数,第二种考虑利用pandas读取csv文件的parse_dates参数,第三种就是常规方法,由于常规方法万金油,所以就从常规方法入手。

    去除“签约时间”字符串,两种方法,一种是使用Seires的map函数中使用lambda匿名函数,另外一种pandas内置的str函数,当然pandas内置的函数更为高效。

# lambda
data.cjshijian.map(lambda x:x[5:])
data.cjshijian.map(lambda x:x.split(":")[1])
#推荐使用内置方法
#大数据量下较为高效
data.cjshijian.str.replace("签约时间:","")

data.cjshijian = data.cjshijian.str.replace("签约时间:","")
data.set_index(data.cjshijian, inplace=True)
data.index

data.index = pd.to_datetime(data.index)
data = data.sort_index()
data.index


    取2015年的前三个季度和第四个季度的数据,注意使用data['2015']这种一个字符串去切片的方法目前是可以的,但是未来会被移除掉

data.loc['2015']
data['2015':'2015']
data3 = data['20150101':'20150930']
data3

data4 = data['20151001':'2015']
data4


    为了让模型具有代表性,所以将数据中成交数量低于5条的小区剔除掉,先看下cjxiaoqu的数据是否规整

(data3.cjxiaoqu.map(lambda x:len(x.split())) !=3).sum()
>1

    说明有些变量内容长度不为3,将其先排除掉

data3 = data3[data3.cjxiaoqu.map(lambda x:len(x.split())) ==3]

    新增一个特征“小区”

data3 = data3.assign(xiaoqu =
             data3.cjxiaoqu.map
             (lambda x:x.split()[0])
            )


 

records = data3['xiaoqu'].value_counts()
records

bigger5 =records[records>5]
bigger5

    此时取出数据中成交记录大于5条的小区

data3=data3[data3.xiaoqu.isin(bigger5.index)]


    由于需要计算第4个季度的房价涨幅,所以需要先算得前三个季度的cjdanjia的平均值,再利用(4季度单价平均值-3季度均值)/3季度均值获得涨幅:

# 前三个季度的cjdanjia的平均值
data3_mean = data3.groupby('xiaoqu')['cjdanjia'].mean()
data3_mean


    对于第4季度的数据data4,如果我们也按照之前的方法进行处理

data4 = data4[data4.cjxiaoqu.map(lambda x:len(x.split())) ==3]
data4 = data4.assign(xiaoqu =
             data4.cjxiaoqu.map
             (lambda x:x.split()[0])
            )
records_2 = data4['xiaoqu'].value_counts()
bigger55 =records_2[records_2>5]
data4 = data4[data4.xiaoqu.isin(bigger55.index)]
data4

data4_mean = data4.groupby('xiaoqu')['cjdanjia'].mean()
data4_mean


    但是,存在一个问题,就是前三季度成交量大于5条的小区未必在第四季度成交量也大于5条,来看下例子:

(data4_mean -data3_mean)/data3_mean

    

    就比如其中的“龙锦苑二区”,在第4个季度就不存在交易


    所以在计算成交记录大于5条时,我们应该选择前三个季度的小区records

#前面的代码几乎一样
data4 = data4[data4.cjxiaoqu.map(lambda x:len(x.split())) ==3]
data4 = data4.assign(xiaoqu =
             data4.cjxiaoqu.map
             (lambda x:x.split()[0])
            )
#前三个季度成交量大于5条
records = data3['xiaoqu'].value_counts()
bigger5 =records[records>5]

data4 = data4[data4.xiaoqu.isin(bigger5.index)]
data4

data4_mean = data4.groupby('xiaoqu')['cjdanjia'].mean()
increase = (data4_mean -data3_mean)/data3_mean
increase


    我们现在想要获取涨幅在10%以上的小区,并将其设置为目标值,建立相关特征,首先将increase转化成DataFrame,方法很多,以下两种方法均可

increase.to_frame()
pd.DataFrame(increase)

increase = increase.dropna()
increase = increase.to_frame()

    可以看到这个列名不对,直接修改,注意,这种方法传入的必须的是一个完整列名的列表

increase.columns = ['up_pct']


    现在,开始为这个DataFrame增加目标,即涨幅是否超过10%

increase.up_pct> 0.1

    返回的布尔值我们可以使用astype方法将其转为数值类型

increase = increase.assign(ten=
        (increase.up_pct> 0.1).astype("int"))
increase


    现在目标有了,可以开始选取特征了,例如成交的套数,小区的单价(似乎单价起点低的涨幅更容易突破),房子的总价等

data3.xiaoqu.value_counts().to_frame()

#合并到新的DF中increase
#但是列名应以新的DF为准
increase = pd.merge(increase,
        data3.xiaoqu.value_counts().to_frame(),
        left_index=True,
        right_index=True,
        how='left')
increase

使用rename函数更改下列名:

increase.rename(columns=
                {'xiaoqu':'taoshu'},
               inplace=True)
increase.drop(columns='up_pct',inplace=True)
increase


    同样,获取单价特征

data3.groupby('xiaoqu')['cjdanjia'].mean()

increase = pd.merge(increase,
        data3.groupby('xiaoqu')['cjdanjia'].mean().to_frame(),
        left_index=True,
        right_index=True,
        how='left')
increase


    同样,获取房价特征

data3.groupby('xiaoqu')['cjzongjia'].mean()

increase = pd.merge(increase,
        data3.groupby('xiaoqu')['cjzongjia'].mean().to_frame(),
        left_index=True,
        right_index=True,
        how='left')
increase

 

    开始准备数据建模,需要注意的是我们的训练数据应该统一选择前三季度的数据,下面的例子中选择的是70%。

permutation的使用

#permutation为每条数据
#都分配了一个乱序索引
index = np.random.permutation(len(increase))
index

#建立70%训练数据
data_train = increase.iloc[index[:int(len(increase)*0.7)]]
data_train

data_test = increase.iloc[index[int(len(increase)*0.7):]]
data_test

x_train,y_train = data_train[list(x for x in data_train.columns if x != 'ten')],\
data_train.ten
x_test,y_test = data_test[list(x for x in data_test.columns if x != 'ten')],\
data_test.ten

    或者直接使用sklearn.model_selection里面的快捷函数

x_train,x_test,y_train,y_test = \
train_test_split(increase[list(x for x in increase.columns if x != 'ten')],
                                                 increase.ten,
                                                test_size=0.3)
x_train

    建立模型

from sklearn.linear_model import LogisticRegression
model = LogisticRegression()
model.fit(x_train, y_train)
from sklearn.metrics import accuracy_score
accuracy_score(y_test, model.predict(x_test))
# model.score(x_test,y_test)


    可以看到准确率只有64%。


    我们也可以再次新增特征,例如:各小区成交的经纪人人数:

data3.groupby('xiaoqu')['xingming'].unique().map(len)

increase = pd.merge(increase,
        data3.groupby('xiaoqu')['xingming'].unique().map(len).to_frame(),
        left_index=True,
        right_index=True,
        how='left')
increase


    其它的流程重新再走一遍:

index = np.random.permutation(len(increase))
data_train = increase.iloc[index[:int(len(increase)*0.7)]]
data_test = increase.iloc[index[int(len(increase)*0.7):]]

x_train,y_train = data_train[list(x for x in data_train.columns if x != 'ten')],\
data_train.ten
x_test,y_test = data_test[list(x for x in data_test.columns if x != 'ten')],\
data_test.ten

model = LogisticRegression()
model.fit(x_train, y_train)

accuracy_score(y_test, model.predict(x_test))

    可以看到,准确率更低了,这也说明特征并非越多越好。