美文网首页数据分析Pandaspython
使用Python Pandas分析处理Airbnb数据

使用Python Pandas分析处理Airbnb数据

作者: 东东隆东抢 | 来源:发表于2019-11-25 17:33 被阅读0次

    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上,欢迎评论、指正。

    相关文章

      网友评论

        本文标题:使用Python Pandas分析处理Airbnb数据

        本文链接:https://www.haomeiwen.com/subject/bwbdwctx.html