【DataFrame常用操作】以影院票房日度数据为例

王校长吃热狗

在处理数据的过程中,经常用到强大的Pandas.DataFrame,由于其参数多自由度高,有时候容易晕头转向。
故将数据清洗、处理和画图中的常用操作总结如下。
不定期更新,欢迎收藏。

这次利用tushare提供的影院日度票房数据接口为例进行演示。


bo_cinema接口介绍

为什么要放王校长吃热狗的图?答案在文中。
(1)准备工作
加载需要的库:

# -*- coding: UTF-8 -*-
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import tushare as ts
from datetime import datetime
import time
import pylab

注册tushare获得‘your token’:

%matplotlib inline
pro = ts.pro_api('your token')
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False
df = pd.DataFrame()

设定jupyter notebook中显示图片的尺寸:

pylab.rcParams['figure.figsize'] = (8, 4.5)

(2)获取数据

def get_data(df, startdate, enddate):
    # 日期格式转换
    tp = [x.strftime('%Y%m%d') for x in list(pd.date_range(start=startdate, end=enddate))]
    print(tp)
    for t in tp:
        #加个延迟避免被ban
        time.sleep(62/40)
        dfdaily = pro.bo_cinema(date = t)
        df = pd.concat([df, dfdaily], axis=0) # 纵向合并
    return df


startdate = '20190205'
enddate = '20190321'
data = get_data(df, startdate, enddate)

输出的日期列表:

    ['20190205', '20190206', '20190207', '20190208', '20190209', '20190210', '20190211', '20190212', '20190213', '20190214', '20190215', '20190216', '20190217', '20190218', '20190219', '20190220', '20190221', '20190222', '20190223', '20190224', '20190225', '20190226', '20190227', '20190228', '20190301', '20190302', '20190303', '20190304', '20190305', '20190306', '20190307', '20190308', '20190309', '20190310', '20190311', '20190312', '20190313', '20190314', '20190315', '20190316', '20190317', '20190318', '20190319', '20190320', '20190321']

查看原始数据前5行:

data.head()
查看原始数据前5行

说明:该数据是按每日票房收入(day_amount)对全国影院进行排名,列出全国排名前100的影院(看看有你去过的没)。
查看data的形状:

data.shape
(3900, 9)

描述性统计:

data.describe().T
描述性统计

(3)进行初步分析
按影院上榜次数计数:

c_name_conunts = data['c_name'].value_counts()
c_name_conunts[:30]

输出前30家:
(看看有你去过的吗~)

金逸北京荟聚IMAX店        39
广州飞扬影城(正佳分店)       39
首都电影院西单店           39
武汉汉街万达广场店          39
UME影城(北京双井店)       39
兰州城关万达广场店          39
武商摩尔国际电影城          39
中影国际影城珠海华发2店       39
Jackie Chan北京耀莱    39
南京新街口国际影城          38
金逸北京大悦城IMAX店       38
兰州中心空间站IMAX影城      38
北京通州万达广场店          37
北京市金泉港国际影城         37
广州市捷登都会飞扬影城        36
金逸天津大悦城IMAX店       36
成都锦华万达广场店          36
南昌红谷滩万达广场店         35
昆明万达影城同德广场店        35
广州飞扬影城             35
首都电影院昌平店           35
深圳市百老汇电影中心影城       34
卢米埃北京长楹天街IMAX影城    34
金逸上海龙之梦IMAX店       33
大连经开万达广场店          33
金逸广州海珠城IMAX店       33
中影国际影城北京昌平永旺店      33
中影国际影城武汉光谷天河店      32
福州仓山万达广场店          32
UME影城(重庆渝中店)       31
Name: c_name, dtype: int64

画简单的横柱状图:

c_name_conunts[:15].plot(kind='barh', rot=0)
output_9_1.png

按影院名称是否含“万达”进行分类,研究万达相关影院的情况:
(强行解释题图)

if_wanda = np.where(data.c_name.str.contains('万达'), 'Wanda', 'Not Wanda')
if_wanda[:5]

查看:

array(['Not Wanda', 'Not Wanda', 'Not Wanda', 'Wanda', 'Not Wanda'], dtype='

想要直接按万达和非万达分组进行与上面类似的计数会有些复杂,这里提供pandas之父麦金尼的方法,创造性的用df.groupby()和.size().unstack()解决:

by_cname_ifwanda = data.groupby(['rank', if_wanda])
agg_counts = by_cname_ifwanda.size().unstack().fillna(0)
agg_counts[:10]

效果如下:


按是否万达分组计数

在万达的电影院,每日排名一般在多少位呢?
画出能体现万达比例的柱形图,可视性更好:

agg_counts[:25].plot(kind='barh', stacked=True)
output_12_1.png

画折线图:

plt.plot(agg_counts['Not Wanda'], label='Not Wanda', linestyle='--')
plt.plot(agg_counts.Wanda, label='Wanda')
plt.plot((0, 100), (18.5, 18.5), color='black', lw =0.5)
plt.legend()

output_13_1.png

(4)用透视表分析
将if_wanda添加到原始数据中形成新的列(即df增加columns),并查看:

data['ifwanda'] = if_wanda
data.head()
新增一列

考察同样排名情况下,万达与非万达的平均票价差距:

mean_stat = data.pivot_table('avg_price', index='rank', columns='ifwanda', aggfunc='mean')
mean_stat['diff']=mean_stat['Wanda']-mean_stat['Not Wanda']
mean_stat[:20].round(2)
相同排名时平均票价差距

画柱状图:

#plt.plot(mean_stat['diff'])
plt.plot((0, 100), (0, 0), color='black', lw =0.5)
plt.bar(mean_stat.index, mean_stat['diff'])
output_17_1.png

直观上,排名相同的情况下,万达的影院要比非万达影院平均票价贵。
求平均票价差距的均值:

mean_stat['diff'].mean()
 3.4035306779919057

即平均贵3.40元。
接下来,从总体角度分析。
创建一个新的数据集,只保留想研究的3个columns:
(通过字典创建)

sum_datadic = {'date': data.date,
               'day_amount': data.day_amount,
               'att_ratio': data.att_ratio,
               'avg_price': data.avg_price}
sum_data = pd.DataFrame(sum_datadic)
sum_data[:10]
sum_data

日常中面板数据比较常用的,按日期求均值:
(注意pivot_table的用法)

mean2 = data.pivot_table(['att_ratio', 'day_amount', 'avg_price'], index='date', aggfunc='mean')
mean2.day_amount = mean2.day_amount/10000
mean2[:10].round(2)
mean2

研究新数据集3列间的关系,
画折线图:

timeline = [datetime.strptime(d, '%Y%m%d').date() for d in mean2.index]
plt.plot(timeline, mean2.att_ratio, label='日均上座率(%)', linestyle=':')
plt.plot(timeline, mean2.day_amount, label='日均单影院收入(万元)',linestyle='--')
plt.plot(timeline, mean2.avg_price, label='日均票价(元)')
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
plt.legend(loc='best')
plt.grid()
上座率和收入呈现明显周末效应,票价虽有波动但不如前两者明显。考虑到定价受多方面因素掣肘,影院想提高收入还得从提高上座率入手。

另一种方法:直接对原始数据按日平均

data_by_day = data.pivot_table(data.columns, index='date', aggfunc='mean')
data_by_day.round(2)[:10]
少了一行

结果发现少了一行(aud_count),原因是该列数据的类型不是整数型或浮点型,不能计算。
类型转换就可以了:

data.aud_count = data.aud_count.astype('int')
data_by_day = data.pivot_table(data.columns, index='date', aggfunc='mean')
data_by_day.round(2)[:10]
重新构造透视表

将data_by_day中的6列数据用折线图表示出来,
画多个子图:

fig, axs = plt.subplots(6, 1)
j=0
timeline = [datetime.strptime(d, '%Y%m%d').date() for d in data_by_day.index]
for col in data_by_day.columns:
    axs[j].plot(timeline, data_by_day[col])
    axs[j].xaxis.set_major_formatter(mdates.DateFormatter('%m%d'))
    #axs[j].xaxis.set_major_locator(mdates.MonthLocator())
    axs[j].set(title=col)
    axs[j].grid()
    j += 1
plt.tight_layout()
#plt.gcf().autofmt_xdate()
画多个子图

发现图像在jupyter notebook里显示的太过细小了,通过下面的语句调整显示图片的尺寸;

pylab.rcParams['figure.figsize'] = (8, 16)

重新画图:

fig, axs = plt.subplots(6, 1)
j=0
timeline = [datetime.strptime(d, '%Y%m%d').date() for d in data_by_day.index]
for col in data_by_day.columns:
    axs[j].plot(timeline, data_by_day[col])
    axs[j].xaxis.set_major_formatter(mdates.DateFormatter('%m%d'))
    #axs[j].xaxis.set_major_locator(mdates.MonthLocator())
    axs[j].set(title=col)
    axs[j].grid()
    j += 1
plt.tight_layout()
#plt.gcf().autofmt_xdate()
效果好多了

(本文基于伟大的麦金尼所著《利用python进行数据分析》和本人粗浅的日常经验。)

你可能感兴趣的:(【DataFrame常用操作】以影院票房日度数据为例)