【DataFrame常用操作】以影院票房日度數(shù)據(jù)為例

王校長吃熱狗

在處理數(shù)據(jù)的過程中,經(jīng)常用到強(qiáng)大的Pandas.DataFrame,由于其參數(shù)多自由度高,有時候容易暈頭轉(zhuǎn)向。
故將數(shù)據(jù)清洗、處理和畫圖中的常用操作總結(jié)如下。
不定期更新,歡迎收藏。

這次利用tushare提供的影院日度票房數(shù)據(jù)接口為例進(jìn)行演示。


bo_cinema接口介紹

為什么要放王校長吃熱狗的圖?答案在文中。
(1)準(zhǔn)備工作
加載需要的庫:

# -*- 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()

設(shè)定jupyter notebook中顯示圖片的尺寸:

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

(2)獲取數(shù)據(jù)

def get_data(df, startdate, enddate):
    # 日期格式轉(zhuǎn)換
    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']

查看原始數(shù)據(jù)前5行:

data.head()
查看原始數(shù)據(jù)前5行

說明:該數(shù)據(jù)是按每日票房收入(day_amount)對全國影院進(jìn)行排名,列出全國排名前100的影院(看看有你去過的沒)。
查看data的形狀:

data.shape
(3900, 9)

描述性統(tǒng)計:

data.describe().T
描述性統(tǒng)計

(3)進(jìn)行初步分析
按影院上榜次數(shù)計數(shù):

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

輸出前30家:
(看看有你去過的嗎~)

金逸北京薈聚IMAX店        39
廣州飛揚影城(正佳分店)       39
首都電影院西單店           39
武漢漢街萬達(dá)廣場店          39
UME影城(北京雙井店)       39
蘭州城關(guān)萬達(dá)廣場店          39
武商摩爾國際電影城          39
中影國際影城珠海華發(fā)2店       39
Jackie Chan北京耀萊    39
南京新街口國際影城          38
金逸北京大悅城IMAX店       38
蘭州中心空間站IMAX影城      38
北京通州萬達(dá)廣場店          37
北京市金泉港國際影城         37
廣州市捷登都會飛揚影城        36
金逸天津大悅城IMAX店       36
成都錦華萬達(dá)廣場店          36
南昌紅谷灘萬達(dá)廣場店         35
昆明萬達(dá)影城同德廣場店        35
廣州飛揚影城             35
首都電影院昌平店           35
深圳市百老匯電影中心影城       34
盧米埃北京長楹天街IMAX影城    34
金逸上海龍之夢IMAX店       33
大連經(jīng)開萬達(dá)廣場店          33
金逸廣州海珠城IMAX店       33
中影國際影城北京昌平永旺店      33
中影國際影城武漢光谷天河店      32
福州倉山萬達(dá)廣場店          32
UME影城(重慶渝中店)       31
Name: c_name, dtype: int64

畫簡單的橫柱狀圖:

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

按影院名稱是否含“萬達(dá)”進(jìn)行分類,研究萬達(dá)相關(guān)影院的情況:
(強(qiáng)行解釋題圖)

if_wanda = np.where(data.c_name.str.contains('萬達(dá)'), 'Wanda', 'Not Wanda')
if_wanda[:5]

查看:

array(['Not Wanda', 'Not Wanda', 'Not Wanda', 'Wanda', 'Not Wanda'], dtype='<U9')

想要直接按萬達(dá)和非萬達(dá)分組進(jìn)行與上面類似的計數(shù)會有些復(fù)雜,這里提供pandas之父麥金尼的方法,創(chuàng)造性的用df.groupby()和.size().unstack()解決:

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

效果如下:


按是否萬達(dá)分組計數(shù)

在萬達(dá)的電影院,每日排名一般在多少位呢?
畫出能體現(xiàn)萬達(dá)比例的柱形圖,可視性更好:

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添加到原始數(shù)據(jù)中形成新的列(即df增加columns),并查看:

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

考察同樣排名情況下,萬達(dá)與非萬達(dá)的平均票價差距:

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

直觀上,排名相同的情況下,萬達(dá)的影院要比非萬達(dá)影院平均票價貴。
求平均票價差距的均值:

mean_stat['diff'].mean()
 3.4035306779919057

即平均貴3.40元。
接下來,從總體角度分析。
創(chuàng)建一個新的數(shù)據(jù)集,只保留想研究的3個columns:
(通過字典創(chuàng)建)

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

日常中面板數(shù)據(jù)比較常用的,按日期求均值:
(注意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

研究新數(shù)據(jù)集3列間的關(guān)系,
畫折線圖:

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()
上座率和收入呈現(xiàn)明顯周末效應(yīng),票價雖有波動但不如前兩者明顯??紤]到定價受多方面因素掣肘,影院想提高收入還得從提高上座率入手。

另一種方法:直接對原始數(shù)據(jù)按日平均

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

結(jié)果發(fā)現(xiàn)少了一行(aud_count),原因是該列數(shù)據(jù)的類型不是整數(shù)型或浮點型,不能計算。
類型轉(zhuǎn)換就可以了:

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]
重新構(gòu)造透視表

將data_by_day中的6列數(shù)據(jù)用折線圖表示出來,
畫多個子圖:

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()
畫多個子圖

發(fā)現(xiàn)圖像在jupyter notebook里顯示的太過細(xì)小了,通過下面的語句調(diào)整顯示圖片的尺寸;

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進(jìn)行數(shù)據(jù)分析》和本人粗淺的日常經(jīng)驗。)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容