使用Python Pandas分析处理Airbnb数据

Pandas是能够让Python成为数据分析编程语言的原因之一,它使得导入、分析和可视化数据变得更加容易。它能够快捷地读取CSVExcelJsonHtmlSQL等文件,其主要的2种数据结构是SeriesDataFrame。Series本质上是一个列,而DataFrame是一个由Series集合组成的多维表。


本文用Airbnb (爱彼迎) 的数据来学习Pandas基础知识并初步探索首都北京的民宿特点。这些数据来源于网络,官网上中国只有2个城市的数据,分别是Beijing和Hong Kong。这里采用关于北京的calendar.csv这个文件,可以通过链接进行下载。

另外本文是使用Python 3.7.4编写的,使用Jupyter Notebook构建的。在演示之前需要导入pandasmatplotlib库:

import pandas as pd 
import matplotlib.pyplot as plt

查看pandas版本,使用下面命令,是双下划线:

pd.__version__ 

我是将calendar.csv文件与工程文件放在同一目录下的,直接根据文件名导入CSV文件:

calendar = pd.read_csv('calendar.csv')  

若不在同一目录下,可以使用其完整路径,路径前的r是为了防止一些转义字符:

calendar = pd.read_csv(r'C:\Users\ringo\Desktop\calendar.csv')

查看前5行数据,作用等价于使用calendar.head(),查看后5行可以使用tail()函数:

calendar[:5]  
随机抽取5行数据
calendar.sample(5) 
查看行数和列数
calendar.shape   

输出形式为元组(rows, columns),在这个数据表中共有 12681641行、7列,数据量之庞大。

查看索引、数据类型和内存信息

info()提供关于数据集的基本细节,比如行和列的数量、非空值的数量、每个列中的数据类型以及DataFrame使用了多少内存。

calendar.info()
显示所有列的数据类型
calendar.dtypes  
isnull的使用

.isnull()本身不是很有用,通常与sum()等其他方法结合使用。

calendar.isnull().sum()

我们可以发现共有194 行Priceadjusted_price列值为null,6 行minimum_nightsmaximum_nights为null。

移除空值

数据分析经常会面临输入值为空的难题,这是一个需要对数据及其上下文有深入了解的决策。一般来说只建议在有少量遗漏的情况下删除空数据。

calendar.dropna()

这个操作将删除至少有一个空值的任何行,但是它将返回一个新的DataFrame,而不改变原来的数据。但我们也可以在这个方法中指定inplace=True,在原有的数据表上直接进行修改。

calendar.dropna(inplace=True)

除了删除行之外,我们还可以通过设置axis=1来删除空值的列。

axis=1是什么参数?

axis从何而来,为什么需要为1才能影响列,这些都不是很明显。查看原因,只需查看.shape输出 (12681441, 7),如上文所述这是一个元组,即12681441行和7列。注意在这个元组的索引0处,而在这个元组的索引1处。这就是为什么axis=1会影响列的原因。

unique 和 nunique的区别?

unique()以数组形式返回列的所有唯一值,而nunique()是返回的是唯一值的个数。

calendar.date.unique()
calendar.date.nunique()
print('有',calendar.date.nunique() , '天' , calendar.listing_id.nunique() ,'不同的清单在calendar中')

有 383 天 34744 不同的清单在calendar中

如何获取列?

我们可以使用方括号['列名']的形式获取列,当然也可以使用前者点语法。

calendar['date'] = calendar.date 
type(calendar.date)

pandas.core.series.Series

这将返回一个 Series,若要将列提取为DataFrame,需要传递列名列表:

date  = calendar[['date']]
type(date)

pandas.core.frame.DataFrame

如何获取行?

一般情况下,有2种方式,根据名称 loc 和根据index数值 iloc。方便演示,我们先来创建一个DataFrame:

demo = pd.DataFrame({'name'   :['Ringo','Jerry','Aliza','Grace','Tonny'],
                     'apples' :[1,3,0,3,6],
                     'oranges':[2,4,6,2,4]})
demo.set_index('name',inplace=True)  #重新设置name列为index

这样我们就得到如下样式的数据表:
demo.loc['Ringo']

另一方面,对于iloc,我们给它Ringo的数值索引0:

demo.iloc[0]

我们还可以按照这样的方式进行多行选择:

demo.loc['Ringo':'Aliza']
demo.iloc[0:3]

注意iloc[0:3]并不能抽取到Grace这行数据,这是因为使用.iloc进行切片与使用列表进行切片遵循相同的规则,不包括位于末尾索引处的对象。

当然我们也可以选择任意列,比如选择前3行,Oranges列:

demo.loc['Ringo':'Aliza',['oranges']]
min()和max()
calendar.date.min()
calendar.date.max()

我们有2019-09-23到2020-10-09超过一年的数据。列为available里的ft分别代表FalseTrue,即房间不可预订和可预订 。
我们来看下不可预订和可预订的比例?使用如下代码可以输出f,t对应的个数。

calendar.available.value_counts()
plt.axes(aspect='equal') # 将横、纵坐标轴标准化处理,保证饼图是一个正圆,否则为椭圆,等同于 plt.axis('equal')

plt.pie(calendar['available'].value_counts(),labels= ['Available','Not Available'],autopct='%.1f%%',radius = 1.2,colors= ['r','g'])

plt.title('Room available ratio') # 设置title

plt.show()

从饼图中可以看出大概还有6成的房间可以预订。


如果将上面的代码修改成如下code,那么饼图将有所改变,explode每一块饼图离开中心距离,默认值为(0,0)就是不离开中心;shadow 是否阴影,默认值为False,即没有阴影;textprops 设置标签(labels)和比例文字的格式,属于字典类型,可选参数,默认值为None

plt.pie(
         calendar.available.value_counts(),
         explode=(0,0.1),  #Not Available区块分离
         labels= ['Available','Not Available'],  #标签
         autopct='%1.1f%%',  #显示占比
         radius = 1.2,  #设置半径
         colors = ['#abcdef','#ccddaf'], #设置颜色
         textprops={'fontsize':14,'color':'black'},  #设置字体、颜色
         shadow = True #显示阴影 
)
plt.show()

另外可以设置成中文显示标签,代码如下,自己去试试吧。

plt.rcParams['font.sans-serif']=['SimHei'] 

接下来我们研究下不同时间段的订房率,将dateavailable两列数据取出生成一个新的DataFrame, 将其命名为new_calendar

new_calendar = calendar[['date','available']]
什么是apply函数?

apply()函数形式如下:

DataFrame.apply(func, axis=0, broadcast=False, raw=False, reduce=None, args=(), **kwds)

func函数需要自己实现,函数的传入参数根据axis来定,比如axis = 1,就会把一行数据作为Series的数据结构传入给自己实现的函数中,我们在函数中实现对Series不同属性之间的计算,返回一个结果,则apply函数会自动遍历每一行DataFrame的数据,最后将所有结果组合成一个Series数据结构并返回。

新增一列available_num,当不可预订时将其值设为1,反之为0。

new_calendar['available_num'] = calendar['available'].apply(lambda x:1 if x =='t' else 0)

当然此处我们也可以直接使用map()函数,效果是一样的:

new_calendar['available_num'] = calendar['available'].map({'f':0,'t':1})
SettingWithCopyWarning

运行后发现弹出一条SettingWithCopyWarning警告:


可以参考网上SettingwithCopyWarning 的原理和解决方案,我们可以进行copy()操作或者采用loc方法。

new_calendar = calendar[['date','available']].copy()

new_calendar = calendar.loc[:,['date','available']]
new_calendar[:5]
什么是groupby?

groupby()操作一般涉及拆分对象(Splitting)、应用函数(Applying)以及组合结果(Combining)的组合。它可以用于对大量数据进行分组,并在这些组上进行计算操作。如组内计数、求和、求均值以及求方差等。
Splitting —— 通过对数据集应用一些条件将数据分组;
Combining —— 将一个函数独立地应用于每个组;
Combining —— 将groupby和结果应用到数据结构中,然后合并不同的数据集。

根据不同日期进行分组,求计算已经预定的平均值:

new_calendar = new_calendar.groupby('date')[['available_num']].mean()
new_calendar.rest_index(inplace = True)
new_calendar[:5]

使用双括号索引[['available_num']]是为了直接自动返回一个DataFrame对象,这样index会直接变成date,使用reset_index()后又可以重新生产index,date变成列。

什么是dt和str?

Series对象和DataFrame的列数据提供了catdtstr三种属性接口,分别对应分类数据、日期时间数据和字符串数据。
DataFrame数据中的日期时间列支持dt接口,该接口提供了dayofweekdayofyearis_leap_yearquarterweekday_name等属性和方法,DataFrame数据中的字符串列支持str接口,该接口提供了centercontainscountendswithfindextractlowersplit等大量属性和方法,大部分用法与字符串的同名方法相同,少部分与正则表达式的用法类似。

时间处理to_datetime()函数
new_calendar['date'] = pd.to_datetime(new_calendar.date,format = '%Y%m%d') 
图表显示
plt.figure(figsize = (10 , 8))
plt.plot(new_calendar['date'] , new_calendar['available_num']*100)
plt.title('Airbnb Beijing Calendar')
plt.ylabel('Room available rate (%)')
plt.show()

图中可见今年国庆节前后房间可预订率明显下降,说明很多人在国庆出游订房。到了2020年元旦时可预订率又直线下降。但明年4月过后为何又那么多人订房?是春游、暑假吗?


接下来再来看看北京哪个月的民宿较为便宜?

calendar[:5]     #查看前5行
calendar[-5:]  #查看后5行


发现price列里的数据有,$符号,我们需要将其统一替换掉,另外使用info()函数发现price列是object,需要将其强转成float类型.

calendar['price'] = calendar['price'].str.replace(',','').str.replace('$','').astype(float)
什么是strftime?

strftime函数是将字符串按照后面的格式转换成时间元组类型。

mean_price_of_month = calendar.groupby(calendar['date'].dt.strftime('%b') , sort = False)['price'].mean()
mean_price_of_month.plot('bar',figsize=(12,7))
plt.ylabel('Average monthly price')
plt.xlabel('Month')
plt.show()

下图可以观察到每个月的北京的民宿平均价格差异不大,没有明显的淡旺季之分。

最后再来看看每天的平均价格如何?

calendar['dayofweek'] =calendar.date.dt.weekday_name
weekday = calendar['dayofweek'].unique().tolist()

['Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday', 'Monday']
没有按照周一到周日的顺序,手动调整数组元素顺序:

weekday = ['Monday','Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' ]

使用groupby按照周几进行分组,再对价格求平均值并进行图表显示。

price_of_weekday = calendar.groupby('dayofweek')['price'].mean().reindex(weekday)
plt.figure(figsize=(10,8))
plt.plot(price_of_weekday,linewidth=3, color='orange',marker= 'o',markerfacecolor='r', markersize= 8)
plt.xlabel('Day of week')  #x轴坐标
plt.ylabel('Price(Yuan)')   #y轴坐标
plt.title('Average Price of Weekday')  #设置标题
plt.grid()  #显示网格

跟预期结果一样,周五和周六民宿价格要稍贵一些。但是平均价格都在700+元以上,这个感觉有点不符合实际情况。

最后将code放在GitHub_ Pandas_tutorial上,欢迎评论、指正。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,294评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,780评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,001评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,593评论 1 289
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,687评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,679评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,667评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,426评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,872评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,180评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,346评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,019评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,658评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,268评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,495评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,275评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,207评论 2 352

推荐阅读更多精彩内容