引入使用的包
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import sklearn as sk
%matplotlib inline
import datetime
import os
import seaborn as sns
读取文件
train = pd.read_csv("train_users_2.csv")
test = pd.read_csv("test_users.csv")
观察数据
1.数据含义
train.head(5)
test.head(5)
从train和test文件中观察到数据train有16个feature,test有15个feature, 其中是因为test文件中少了country_destination(订房国家)这一feature,也是我们需要去预测的。 前面15个feature的含义分别是:
feature | 含义 |
---|
id | 用户的id | date_account_created | 账号生成时间 | timestamp_first_active | 账号激活时间 | data_first_booking | 第一次订房时间 | gender | 性别 | age | 年龄 | signup_method | 注册方式 | language | 使用的语言 | affiliate_channel | 付费市场渠道 | affiliate_provider | 付费市场渠道名称 | first_affiliate_tracked | 注册前第一个接触的市场渠道 | signup_app | 注册的app | first_device_type | 设备类型 | first_brower | 浏览器类型 |
2.观察一下数据量的大小类型和缺失值情况
print(train.shape)
print(test.shape)
print(train.info())
print('-----------------------------------------')
print(train.isnull().sum(axis=0))
print(test.info())
print('-----------------------------------------')
print(test.isnull().sum(axis=0))
从数据大小上来看:train中共有213451条数据,test里共有62096条数据。 从空值上来看:2个数据集都分别在一些feature中有空值的情况,分别是:
空值特征数量 | train | test |
---|
date_first_booking | 124543 | 62096 | age | 87990 | 28876 | first_affiliate_tracked | 6065 | 20 |
从数据类型上来:date_account_created,timestamp_first_active,date_first_booking数据类型分别是object,int64和object。 但是通过我们仔细观察数据,这三个feature都是描述时间的,所以我们考虑把它们转换为datatime格式的来操作。
数据探索
date-accout_created(账号注册生成时间)
print(train.date_account_created.value_counts())
print(train.date_account_created.value_counts().describe())
print(test.date_account_created.value_counts())
print(test.date_account_created.value_counts().describe())
把汇总后可以发现: 看起来数据基本正常,没有异常值 train dataset里的账号生成时间分布在1634个不同的天数内,大约是四年半;test dataset里分布了92个不同的天数,大概3个月。 而且看起来train dataset里的天数显示是从2010年到2014年5月,而恰好test dataset里显示从2014年7月开始,那么我们可以猜测这俩个数据集或许就是依赖账号生成时间而进行分割的。我们需要证明我们的猜想。 首先来看一下时间线是不是按照我们想想的情况发生的: 这里我们需要先把数据的类型进行转换,用pandas的to_datetime()函数把其转换成时间数据类型,这样我们能更容易看出数据的情况:
train["date_account_created"] = pd.to_datetime(train.date_account_created)
test["date_account_created"]= pd.to_datetime(test.date_account_created)
print ("Train数据显示第一天: {},最后一天:{}".format(train.date_account_created.min(),train.date_account_created.max()))
print ("Test数据显示第一天: {},最后一天:{}".format(test.date_account_created.min(),test.date_account_created.max()))
Train数据显示第一天: 2010-01-01 00:00:00,最后一天:2014-06-30 00:00:00 Test数据显示第一天: 2014-07-01 00:00:00,最后一天:2014-09-30 00:00:00 利用datetime后转变的时间类型我们可以直接进行画图,看一下随着日期的变动每天的date_account_created用户注册量有怎样的呈现。
sns.set_style('whitegrid')
fig = plt.figure(figsize=(12, 6))
train.date_account_created.value_counts().plot('line')
test.date_account_created.value_counts().plot('line')
plt.xlabel('Year')
plt.ylabel('Count created')
plt.title('Accounts created vs Year')
我们可以看到随着时间的推移,账户的注册量越来越多,这一点证明了Airbnb的注册新用户是一个非常好的线性增长。 同时我们需要注意有一些峰值的出现,是否说明订房日期和节假日星期天等有所关联呢?我们还需要看到一个根据星期天来划分的feature。
timestamp_first_active(账号激活时间)
我们还是首先观察数据类型,我们记得timestamp_first_active数据看起来像是一个时间记录的数据,不过它是一个object类型,我们需要转换。
print(train.timestamp_first_active.value_counts())
print(train.timestamp_first_active.value_counts().describe())
print(test.timestamp_first_active.value_counts())
print(test.timestamp_first_active.value_counts().describe())
我们可以发现每一个通过这样观察每一个数据都是一个比较独立的values,看起来是年月日时分秒的一个形式,那么我们需要转换一下
train['date_first_active'] = pd.to_datetime(train.timestamp_first_active // 1000000, format = '%Y%m%d')
test['date_first_active'] = pd.to_datetime(test.timestamp_first_active // 1000000, format = '%Y%m%d')
再接着检查数据:
train.dtypes
print(train.date_first_active.value_counts())
print(train.date_first_active.value_counts().describe())
train.date_first_active.isnull().sum()
print(test.date_first_active.value_counts())
print(test.date_first_active.value_counts().describe())
好了我们给把时间戳形式的这一类数据转换成了时间类型的数据,然后确定数据数值基本没有异常,进行画图。
sns.set_style('whitegrid')
plt.figure(figsize=(12, 6))
train.date_first_active.value_counts().plot('line')
test.date_first_active.value_counts().plot('line')
plt.xlabel('Year')
plt.ylabel('First actived')
plt.title("First actived vs Year")
可以发现timestamp_first_active和date_account_created数据表现基本类似。都是随着时间的推移,注册量和激活量显著增加。同样存在峰值,在做特征工程的时候需要注意把时间这部分数据分成几个小的feature。
data_first_booking(第一次订房时间)
train.date_first_booking.describe()
test.date_first_booking.describe()
第一次订房时间在train里存在一半以上的空值,在test dataset里更是全部是空值,怕由于我们改动给未来模型增加noise,所以我们会选择删除这个feature。
gender(性别)
我们之前观察性别里有存在unknown的情况,那我们先看一下这一feature下有几个类型的数据。
print('Train dataset中性别分布为:\n ', train.gender.value_counts(dropna = False))
print('Test dataset中性别分布为:\n ',test.gender.value_counts(dropna = False))
unknown的值有很多,尝试把它们变成空值进行处理。
train.gender.replace('-unknown-', np.nan, inplace=True)
test.gender.replace('-unknown-', np.nan, inplace=True)
sns.set_style('ticks')
plt.figure(figsize=(8, 6))
plt.subplot(1,2,1)
plt.bar(train.gender.value_counts().index,train.gender.value_counts(),color=['#073515','#327353','#107850','#529214'])
for i in range(3):
dd = train.gender.value_counts().index[i]
ff = train.gender.value_counts().values[i]
plt.text(dd,ff+50,'%.0f' % ff, ha='center', va='bottom', fontsize=10)
plt.xlabel('Gender')
plt.ylabel('Number')
plt.title("Train dataset")
plt.subplot(1,2,2)
plt.bar(test.gender.value_counts().index,test.gender.value_counts(),color=['#073515','#327353','#107850','#529214'])
for i in range(3):
dd = test.gender.value_counts().index[i]
ff = test.gender.value_counts().values[i]
plt.text(dd,ff+50,'%.0f' % ff, ha='center', va='bottom', fontsize=10)
plt.xlabel('Gender')
plt.ylabel('Number')
plt.title("Test dataset")
plt.subplots_adjust(left=0.04, top=0.96, right = 0.96, bottom = 0.04)
我们用柱状图可视化一下,画图的步骤可能有些繁琐,以后加以改进。通过图中我们可以看出除了空值的话,女性比男性要多一些,那么我们也想到了各个国家的风俗习惯可能会有所差异,一起去旅游度假的时候,有的国家男性订房比较多,有的国家女性来掌管这项事务,我们针对于性别和订房国家来看一下他们之间的关系。又由于我们的数据条件下,训练集中有订房国家,所以这里我们只采用训练集来看一下这俩个特征之间的关系情况。
plt.figure(figsize=(10, 6))
female = sum(train.gender == 'FEMALE')
male = sum(train.gender == 'MALE')
other = sum(train.gender == 'OTHER')
Nan = sum(train.gender.isnull())
female_destinations = train.loc[train.gender == 'FEMALE', 'country_destination'].value_counts()/female *100
male_destinations = train.loc[train.gender == 'MALE', 'country_destination'].value_counts() /male *100
other_destinations = train.loc[train.gender == 'OTHER', 'country_destination'].value_counts()/other*100
nan_destinations = train.loc[train.gender.isnull(), 'country_destination'].value_counts()/Nan*100
female_destinations.plot('bar', width = 0.15, color = '#FF8B8B', position = 0, label = 'Female', rot = 0)
male_destinations.plot('bar', width = 0.15, color = '#0E38B1', position = 1, label = 'Male', rot = 0)
other_destinations.plot('bar', width = 0.15, color = '#61BFAD', position = 2, label = 'Other', rot = 0)
nan_destinations.plot('bar', width = 0.15, color = '#61145D',position = 3, label = 'Nan', rot = 0)
plt.legend()
plt.xlabel('Destination Country')
plt.ylabel('Percentage Of Booking')
plt.show()
结论并不如我们期待那么乐观,基本上男女订房比例是在一个相似的水平没有很明显的区别,F(Not Destination Found;无目的地)这一类别中空值比例很高
Age(年龄)
我们之前查看空值的时候记得age在train和test里面都有空值的情况,我们再来看一下数据描述。
print(train.age.value_counts())
print(train.age.describe())
print(test.age.value_counts())
print(test.age.describe())
出现了最大年龄2014,2002的值,这一点数据异常值很多,我们猜测是把出发日期或者是出生日期填错了,根据我们的经验我们需要年龄设置一个有效区间。 根据人类最长寿命者Jeanne Calment的122岁到18岁的合法身份,我们先观察一下异常数据情况:
print('Train中Age异常值:')
print(sum(train.age>122)+sum(train.age<18))
print('Test中Age异常值:')
print(sum(test.age>122)+sum(test.age<18))
print(train[train.age > 122]['age'].describe())
print(test[test.age > 122]['age'].describe())
pd.set_option('display.max_columns',1000)
pd.set_option('display.max_rows',300)
25/50/75分位数显示Train中2014这个值比较多,和我们猜想填错注册时间基本验证,训练集是从2010年的数据开始那么我们看一下age大于2009的有多少:
train[train.age > 2009].age
占据异常值的大部分,我们把它们转换为空值后,我们再看一下其他大于122岁的:
train.loc[train.age > 2009, 'age'] = np.nan
train[train.age > 122].age
除了150和132这俩个数据比较特殊外,其他的数据像是出生年份,我们用Train中平均注册年份2012来减去这异常值,还原年龄。
train.age[train.age>122]= train.age[train.age>122].apply(lambda x:2012-x)
对于test数据用类似的方法,数据产生的日期年份(2014年)减去这一类异常值,还原年龄:
test.age[test.age>122]= test.age[test.age>122].apply(lambda x:2014-x)
看一下小于18岁的异常值:
print(train[train.age <18]['age'].describe())
print(test[test.age <18]['age'].describe())
从train和test数据表示16岁以上订房人群在平台或许是被准许的,所以我们可以把区间订的下限再降低一些。 我们可以根据常识再最后把年龄数据大于95,小于13的转换为空值:
train.loc[train.age > 95, 'age'] = np.nan
train.loc[train.age < 13, 'age'] = np.nan
test.loc[test.age > 95, 'age'] = np.nan
test.loc[test.age < 13, 'age'] = np.nan
我们将处理好的Age年龄数据可视化一下,看一下分布情况:
sns.set_style('ticks')
plt.figure(figsize=(20, 6))
plt.subplot(1,2,1)
sns.distplot(train.age.dropna(), color='#FD5C64')
plt.xlabel('Train Age')
sns.despine()
plt.subplot(1,2,2)
sns.distplot(test.age.dropna(), color='#055A5B')
plt.xlabel('Test Age')
sns.despine()
从上图可以看出明显订房年龄在25岁到40岁中间人数最多。那么我们再看一下年轻人和老年人是否对于目的地有所不同呢?我们用45岁来区别一下老年人和年轻人:
age = 45
width = 0.4
plt.figure(figsize=(10, 6))
younger = sum(train.loc[train['age'] < age, 'country_destination'].value_counts())
older = sum(train.loc[train['age'] > age, 'country_destination'].value_counts())
younger_destinations = train.loc[train['age'] < age, 'country_destination'].value_counts()/younger * 100
older_destinations = train.loc[train['age'] > age, 'country_destination'].value_counts()/older * 100
younger_destinations.plot(kind='bar',width = 0.4, color='#20AD65', position=0, label='Youngers', rot=0)
older_destinations.plot(kind='bar', width = 0.4,color='#3A745F', position=1, label='Olders', rot=0)
plt.legend()
plt.xlabel('Destination Country')
plt.ylabel('Percentage')
sns.despine()
plt.show()
print(train.age.isnull().value_counts())
print(test.age.isnull().value_counts())
按照45岁来区分的话,可以看出年轻人更多选择在美国旅行,老人出国旅行的意愿更强烈。但是我们还是要记得在年龄数据中大概有45%的空值。
Signup_method(注册方式)
print(train.signup_method.value_counts())
print(test.signup_method.value_counts())
关于注册方式明显是一个分类的feature,看出大多数是basic,自建站的意思,我们可视化一下:
plt.figure(figsize=(10, 6))
plt.subplot(1,2,1)
train.signup_method.value_counts().plot(kind='bar',color='#FB9B2A')
plt.subplot(1,2,2)
test.signup_method.value_counts().plot(kind='bar',color='#674B74')
weibo 应该是中国注册的,其余这一feature没有缺失值,也没有异常值。
Signup_flow(注册页面)
print(train.signup_flow.value_counts())
print(test.signup_flow.value_counts())
我们能看到用户从各个不同的页面进入到注册页面进行注册的,有很多个类别,大多数是代号‘0’的页面。
plt.figure(figsize=(10, 6))
plt.subplot(1,2,1)
train.signup_flow.value_counts().plot(kind='bar',color='#FB9B2A')
plt.subplot(1,2,2)
test.signup_flow.value_counts().plot(kind='bar',color='#674B74')
Language(语言)
print(train.language.value_counts())
print(test.language.value_counts())
既然想要预测客户在哪里订房,语言这一特征必然起到很大作用,我们可以看一下。
plt.figure(figsize=(15, 6))
plt.subplot(1,2,1)
train.language.value_counts().plot(kind='bar',color='#FB9B2A')
plt.subplot(1,2,2)
test.language.value_counts().plot(kind='bar',color='#674B74')
基本上大多数是使用英语,这也在我们意料之中,毕竟Airbnb在United Stated本国推广程度更高一些。
Affiliate_Channel (付费市场渠道)
print(train.affiliate_channel.value_counts())
print(test.affiliate_channel.value_counts())
plt.figure(figsize=(15, 6))
plt.subplot(1,2,1)
train.affiliate_channel.value_counts().plot(kind='bar',color='#FB9B2A')
plt.subplot(1,2,2)
test.affiliate_channel.value_counts().plot(kind='bar',color='#674B74')
各种引流渠道中,大多数是直接的用户引流过来的
Affiliate_Provider (付费市场渠道名称)
print(train.affiliate_provider.value_counts())
print(test.affiliate_provider.value_counts())
plt.figure(figsize=(15, 6))
plt.subplot(1,2,1)
train.affiliate_provider.value_counts().plot(kind='bar',color='#FB9B2A')
plt.subplot(1,2,2)
test.affiliate_provider.value_counts().plot(kind='bar',color='#674B74')
除了直接过来的客户外,google引流渠道的效果很不错
First_Affiliate_Tracked (注册前第一个接触的市场渠道)
train.first_affiliate_tracked.isnull().sum()
test.first_affiliate_tracked.isnull().sum()
print(train.first_affiliate_tracked.value_counts())
print(test.first_affiliate_tracked.value_counts())
plt.figure(figsize=(15, 6))
plt.subplot(1,2,1)
train.first_affiliate_tracked.value_counts().plot(kind='bar',color='#FB9B2A')
plt.subplot(1,2,2)
test.first_affiliate_tracked.value_counts().plot(kind='bar',color='#674B74')
没接触过占大多数,其余的在lined(领英)上获取消息的比较多。
Signup_App(注册APP)
print(train.signup_app.value_counts())
print(test.signup_app.value_counts())
plt.figure(figsize=(15, 6))
plt.subplot(1,2,1)
train.signup_app.value_counts().plot(kind='bar',color='#FB9B2A')
plt.subplot(1,2,2)
test.signup_app.value_counts().plot(kind='bar',color='#674B74')
可以发现大多数使用Web端登陆的。
First_Device_Type(设备类型)
print(train.first_device_type.value_counts())
print(test.first_device_type.value_counts())
plt.figure(figsize=(15, 6))
plt.subplot(1,2,1)
train.first_device_type.value_counts().plot(kind='bar',color='#FB9B2A')
plt.subplot(1,2,2)
test.first_device_type.value_counts().plot(kind='bar',color='#674B74')
台式苹果的使用度比较高,而且苹果系列产品使用情况都挺高的
First_Browser (浏览器类型)
print(train.first_browser.value_counts())
print(test.first_browser.value_counts())
plt.figure(figsize=(20, 5))
plt.subplot(1,2,1)
train.first_browser.value_counts().plot(kind='bar',color='#FB9B2A')
plt.subplot(1,2,2)
test.first_browser.value_counts().plot(kind='bar',color='#674B74')
我们这个train.csv数据探索就结束啦~
|