怎样让Python推荐电影 - DataFrame Merge

上一篇文章介绍了怎样使用tushare金融数据源,统计近10天上映电影的观影人数走势图。程序员上手写Python,常带有Java风格。比如通过Dict字典模拟HashMap,定义各种数据结构,代码中随处可见多层for循环等,虽然运行速度确实快了点,但没有体现出Python长于验证想法的高生产力特质。
本文通过使用Pandas DataFrame对象内置的数据处理功能,以数据分析人员的视角编码(类似Matlab代码风格),将上一篇文章的代码行数压缩了50%,体现了Python和Pandas的表达能力。

为什么选择 DataFrame:

tushare金融数据源接口返回的多数是Pandas的DataFrame对象,以电影票房接口为例,返回指定日期当天的DataFrame格式如下,包括在映所有影片的名称,票房,上映天数等信息。如果查询最近10天的票房信息,下面的DF对象会有10个:

IndexMovieNameBoxOfficeMovieDay
0狮子王16003
1蜘蛛侠150012
2千与千寻30020

绘图函数Matplotlib.plt(xList,yList)的输入参数,横坐标是日期列表,纵坐标是对应日期的票房列表,就是按从左到右,从旧到新的顺序排列最近几天的票房数据,所以我希望的DataFrame格式如下表。只要按行遍历,就可以定位并取出DF对象的一行记录,作为绘图函数的纵坐标输入参数:

IndexMovieName07-1107-1207-13
0狮子王Nan9001600
1蜘蛛侠130012001500
2千与千寻500Nan300

那么怎么跨越上面两个DF对象的格式鸿沟呢?根据官网描述,可使用DataFrame对象内置的列合并函数Merge实现:

DataFrame.merge(right, how=‘inner’, on=None, left_on=None, right_on=None, left_index=False, right_index=False, sort=False, suffixes=(’_x’, ‘_y’), copy=True, indicator=False, validate=None)
Merge DataFrame or named Series objects with a database-style join.
The join is done on columns or indexes. If joining columns on columns, the DataFrame indexes will be ignored. Otherwise if joining indexes on indexes or indexes on a column or columns, the index will be passed on.

官网介绍Merge函数的输入参数how,可以设置为inner, left, right, outer四种类型,完全等同我们所熟悉的SQL内连接,左连接,右连接和全连接。

how : {‘left’, ‘right’, ‘outer’, ‘inner’}, default ‘inner’
Type of merge to be performed.
left: use only keys from left frame, similar to a SQL left outer join; preserve key order.
right: use only keys from right frame, similar to a SQL right outer join; preserve key order.
outer: use union of keys from both frames, similar to a SQL full outer join; sort keys lexicographically.
inner: use intersection of keys from both frames, similar to a SQL inner join; preserve the order of the left keys.

根据介绍tushare接口返回的单日期多个DF对象,通过merge函数,指定连接方式how=outer外连接,关联字段on=MovieName,可以合并为多日期单个DF对象,用于Matplotlib画图。下面是按日期导出的2个DF对象:

IndexMovieName 【关联字段】07-11
0爱宠大机密2300
1蜘蛛侠1300
2千与千寻500
IndexMovieName 【关联字段】07-12
0狮子王900
1蜘蛛侠1200
2扫毒2800

然后用Merge函数把上面2个DF对象全连接成1个DF对象:

IndexMovieName07-1107-12
0狮子王Nan900
1蜘蛛侠13001200
2千与千寻500Nan
3扫毒2Nan800
4爱宠大机密2300Nan

需要注意的是,合并后在原来两个DF中不存在的Cell值,都会自动填上Nan。类似的DF合并函数还有按行合并的Concat,append,join等,具体可参考官方文档。

话不多说上代码:

下面代码用到DataFrame的6类数据处理函数,具体用法参考Pandas官网

  • 行列数获取shape,
  • 多列截取[‘colName1’,‘colName2’],
  • 局部行列截取loc[index,[‘colName1’]],iloc[index, col_index],
  • 元素提取at[index,[‘colName1’]],
  • 指定列重名rename(columns={‘oldColName’:‘newColName’}),
  • 多个DataFrame对象全连接merge
import tushare as ts, datetime
import pandas as pd
import matplotlib.pyplot as plt

def getDateList(days):
# 输入参数days表示指定向前取的天数
    date_list = [] # 返回列表形如['2019-06-29',...]
    for i in range(days,0,-1): #从days开始每次减1
        strDate = (datetime.date.today() \
            - datetime.timedelta(days=i)) \
            .strftime('%Y-%m-%d')  
        # 日期格式:YYYY-MM-DD
        date_list.append(strDate)
    return date_list

def getMovieData(date_list):# 入参['2019-06-29',...]
    firstDate = date_list[0] #得到最早一天
    dfMovie = ts.day_boxoffice(firstDate) #得到票房
    dfResult = dfMovie[['MovieName','BoxOffice']]
    # 将dataframe字段BoxOffice重名为当天日期
    dfResult = dfResult.rename(\
        columns={'BoxOffice':firstDate})
    # 遍历从第二天开始的日期列表
    for date in date_list[1:]:
        dfMovie = ts.day_boxoffice(date) # 取指定日票房
        df = dfMovie[['MovieName','BoxOffice']]# 取2列
        # 将dataframe字段BoxOffice重名为当天日期
        df = df.rename(columns={'BoxOffice':date})
        # 把遍历到的当天df全连接out_join到结果df中
        dfResult = pd.merge(dfResult, df,\
            how='outer', on='MovieName')# on是外键
    return dfResult
    
# 根据入参dataframe对象绘图
def drawLine(dfResult,date_list):
    plt.rcParams['font.sans-serif']\
        =['SimHei'] # 用于正常显示中文标签
    # 遍历结果dataframe的每一行,即遍历每部电影
    for i in range(dfResult.shape[0]):# 0-行数 1-列数
        movieName = dfResult.at[i,'MovieName']
        # 取出第i行第0个name字段右侧的所有字段,转为list
        boxOfficeList = dfResult.iloc[i,1:].tolist()
        # 将list中的字符元素全部转换为float元素
        boxOfficeList = list(\
        	map(float, boxOfficeList))
        dateList = [d[5:] for d in date_list]# 剔除年份
        # 最近上映的每一部电影都绘制一条折线
        plt.plot(dateList, boxOfficeList,\
            label=movieName)

    plt.legend() # 添加折线说明,loc默认best
    plt.ylabel('票房(万)')
    plt.title('电影票房走势图')
    plt.show() # 显示图形
    
if __name__ == '__main__':
    days = 10 # 经测试旧版tushare不提供10天前数据
    # 得到最近几天日期字符列表,格式:YYYY-MM-DD
    date_list = getDateList(days)
    # 返回dataframe对象,行是电影,列是指定日期票房
    dfResult = getMovieData(date_list)
    # 根据函数getMovieData返回的dataframe对象绘图
    drawLine(dfResult, date_list)

代码行数从之前的115行减少到62行,运行后显示如下:
电影票房走势图


往期文章:
怎样让Python推荐电影 - 初试tushare
怎样让Python处理邮件 - 规则轮询
怎样让Python处理邮件 - 初试exchangelib

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值