pandas数据分析和pyecharts可视化周杰伦MV弹幕(多图长文)
周杰倫MV彈幕
- 數據來源:B站爬蟲
- 導入模塊包
- 1. 中文分詞庫 jieba
- 2. 數據分析包 pandas
- 3. 可視化包 matplotlib
- 4. 交互可視化包 pyecharts
- 5. 正則表達式 re
- 1. 數據預處理
- 1.1 讀取數據
- 1.2 查看數據描述信息
- 1.3各列數據的含義
- 1.4 抽樣查看數據
- 1.5 查看前三行數據
- 1.6 查看后三行數據
- 1.7 查看重復數據
- 1.8 清洗數據
- 2. 數據提取和可視化
- 2.1 專輯模塊
- 2.1.1 文件名中提取歌名和專輯名
- 2.1.2 查看各專輯的彈幕數量(存在單曲的,只選擇 1st 2nd 等開頭的專輯)
- 2.1.3 各專輯彈幕數
- 2.1.4. 各專輯的用戶參與人數
- 2.1.5. 開屏彈幕最多的專輯(小于1秒)
- 2.1.6 將參與人數,總彈幕數,開屏彈幕數合成一個新表
- 統計各個專輯的顏色數目
- 2.1.7 繪制雷達圖
- 3. 歌曲模塊
- 3.1. 每首歌的詞云圖
- 3.2. 歌曲的彈幕顏色分布
- 3.3. 歌曲的彈幕模式
- 3.4. 歌曲的彈幕發表時刻
- 3.5 對彈幕高峰時刻查看彈幕模式
- 3.6 對彈幕高峰時刻查看內容詞云圖
- 3.7 對彈幕高峰時刻查看顏色圖
- 4. 用戶模塊
- 4.1 用戶發送的彈幕總數
- 4.2.用戶最常發表的彈幕關鍵詞
- 4.3 用戶最愛發彈幕的前N首歌曲
- 4.4. 用戶發送彈幕的時間
- 4.5. 用戶發送的顏色占比
- 4.6 統計包含關鍵詞的次數(一個用戶在單首歌只統計一次)
- 4.7 統計包含關鍵詞的次數(一個用戶在單首歌統計多次)
本文涉及pandas和pyecharts方法庫使用,re和jieba庫輔助文本處理
數據來源:B站爬蟲
【經典】周杰倫全MV 【193P】
之前B站的彈幕接口可以使用,后來接口更新了,得到的彈幕是亂碼
新的數據接口每首歌曲最多收集一千條,舊接口可以根據日期獲取彈幕數據,需要自己對xml文件中信息提取
https://comment.bilibili.com/2154849.xml (2154849是視頻號bid)
導入模塊包
1. 中文分詞庫 jieba
2. 數據分析包 pandas
3. 可視化包 matplotlib
4. 交互可視化包 pyecharts
5. 正則表達式 re
import re import pandas as pd from pyecharts import options as opts from pyecharts.charts import WordCloud import jieba from pyecharts.globals import SymbolType from pyecharts.charts import Grid, Line, Scatter,Pie,Bar import matplotlib # 顯示坐標中的負號 matplotlib.rcParams["axes.unicode_minus"] = False # 顯示中文字符 matplotlib.rc("font", family="SimHei", weight="bold", size=16)1. 數據預處理
1.1 讀取數據
如果內容列存在英文逗號,使用read_csv或出現列數不符合字段數
df = pd.read_csv("./周杰倫MV.csv")1.2 查看數據描述信息
可以看到彈幕模式列出現異常,內容列存在缺失
df.info()1.3各列數據的含義
發表時刻 : 彈幕出現的時間以秒數為單位。
彈幕模式: 彈幕的模式1…3 滾動彈幕 4底端彈幕 5頂端彈幕 6.逆向彈幕 7精準定位 8高級彈幕
字體大小 字號, 12非常小,16特小,18小,25中,36大,45很大,64特別大
顏色 字體的顏色以HTML顏色的十進制為準
時刻 Unix格式的時間戳。基準時間為 1970-1-1 08:00:00
彈幕池 0普通池 1字幕池 2特殊池【目前特殊池為高級彈幕專用】
發送者ID 發送者的ID,用于“屏蔽此彈幕的發送者”功能
彈幕ID 彈幕在彈幕數據庫中rowID 用于“歷史彈幕”功能。
文件名 包含歌名和專輯名,部分歌曲是單曲
內容 彈幕內容
1.4 抽樣查看數據
df.sample(5)1.5 查看前三行數據
df.head(3)1.6 查看后三行數據
df.tail(3)1.7 查看重復數據
df[df.duplicated()]1.8 清洗數據
清理數據一般涉及檢驗重復值,缺失值,異常值
#如果文件名和發送者ID為空,去掉該數據,同時更新df df.dropna(inplace=True,subset=['文件名', '發送者ID']) print(df.shape)數據規模 (303258, 10) 303258行,10列
2. 數據提取和可視化
將pyecharts的餅圖封裝成一個函數 ,詳細需要閱讀官網:pyecharts
def GetPieFig(data:dict,title:str,width="600px",height="400px"):"""use pyecharts to show pie figure-----------data: series into dict title: Figure title"""pie = (Pie(init_opts=opts.InitOpts(width=width,height=height)).add(title,[[i,j] for i,j in data.items()],center=["40%", "40%"],radius=["30%","50%"],label_opts=opts.LabelOpts(is_show=True)).set_global_opts(title_opts=opts.TitleOpts(title=title),legend_opts=opts.LegendOpts(type_="scroll",pos_left="80%",orient="vertical"),toolbox_opts=opts.ToolboxOpts(pos_bottom="10%"),).set_series_opts(label_opts=opts.LabelOpts( formatter="{b}:{c} (ozvdkddzhkzd%)",is_show=True)))return pie彈幕內容存在特殊串
def FilterWords(x):"過濾特殊字符 只返回由中文字母數字和下劃線組成的字符串"x = str(x)if x.startswith("[") and len(x.split(","))>6:return x.split(",")[4][1:-1]return " ".join(re.findall("\w+",x)) df["內容"] = df["內容"].apply(FilterWords)2.1 專輯模塊
2.1.1 文件名中提取歌名和專輯名
df["歌名"] = df["文件名"].str.extract("(\w+)") def getAlbum(x):m = re.search("【([\w ]+)】",x)if m:return m.group(1)else:return x df["專輯"] = df["文件名"].apply(getAlbum) df[["歌名","專輯"]].sample(5)2.1.2 查看各專輯的彈幕數量(存在單曲的,只選擇 1st 2nd 等開頭的專輯)
不需要獲取所有的列
album = df[df["專輯"].str.contains(r'^(\d)')][["發表時刻","時刻","顏色","內容","發送者ID","專輯"]] #r'^(?:531|92|541[6-9])')] album2.1.3 各專輯彈幕數
albumName = album["專輯"].value_counts().index.tolist() albumCount = album["專輯"].value_counts().values.tolist() c = (Bar().add_xaxis(albumName).add_yaxis("周杰倫各專輯彈幕數", albumCount,).reversal_axis().set_series_opts(label_opts=opts.LabelOpts(position="right")).set_global_opts(xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=0)),title_opts=opts.TitleOpts(title="周杰倫各專輯彈幕數", subtitle="水平柱狀圖/解決標簽名字過長的問題"),) ) c.render_notebook()
由此得到周杰倫的14張專輯的彈幕數量(趕緊出新專輯)
2.1.4. 各專輯的用戶參與人數
2.1.5. 開屏彈幕最多的專輯(小于1秒)
方法一:分組
albumDanmu = album[album["發表時刻"]<1].groupby("專輯")["發送者ID"].count()方法二:對特定列計數
albumDanmu = album[album["發表時刻"]<1]["專輯"].value_counts() import matplotlibmatplotlib.rcParams["axes.unicode_minus"] = False matplotlib.rc("font", family="SimHei", weight="bold", size=16)albumDanmu.plot(kind="barh")2.1.6 將參與人數,總彈幕數,開屏彈幕數合成一個新表
統計各個專輯的顏色數目
color_dict = {} for i in albumName:color_dict[i] = album.groupby("專輯").get_group(i)["顏色"].unique().shape[0] albumColor = pd.Series(color_dict) # names: 索引命名 # keys: 列命名 albumTotalDanmu = album["專輯"].value_counts() albumJoin = pd.Series(join_dict) album_merge = pd.concat([albumJoin, albumTotalDanmu, albumDanmu,albumColor],keys=["彈幕參與人數","總彈幕數","開屏彈幕數","顏色數量"],names=["專輯"],axis=1) album_merge2.1.7 繪制雷達圖
#雷達圖的標簽項 schema = [] for name,value in album_merge.max().to_dict().items():schema.append(opts.RadarIndicatorItem(name=name, max_=value)) name1,name2,name3 = "4th 葉惠美","5th 七里香","6th 十一月的蕭邦" ## 獲取所在行的所有值 album1 = album_merge.loc[name1,:] album2 = album_merge.loc[name2,:] album3 = album_merge.loc[name3,:] ## 值轉換為二維數組 v1 = [album1.values.tolist()] v2 = [album2.values.tolist()] v3 = [album3.values.tolist()]import pyecharts.options as opts from pyecharts.charts import Radar"""根據"彈幕參與人數","總彈幕數","開屏彈幕數","顏色數量" 繪制專輯的雷達圖 """ (Radar(init_opts=opts.InitOpts(width="800px", height="600px", bg_color="#CCCCCC")).add_schema(schema=schema,splitarea_opt=opts.SplitAreaOpts(is_show=True, areastyle_opts=opts.AreaStyleOpts(opacity=1)),textstyle_opts=opts.TextStyleOpts(color="dodgerblue"),).add(series_name=name1,data=v1,linestyle_opts=opts.LineStyleOpts(color="#CD0000"),).add(series_name=name2,data=v2,linestyle_opts=opts.LineStyleOpts(color="#5CACEE"),).add(series_name=name3,data=v3,linestyle_opts=opts.LineStyleOpts(color="#05FF00"),).set_series_opts(label_opts=opts.LabelOpts(is_show=True)).set_global_opts(title_opts=opts.TitleOpts(title="基礎雷達圖",subtitle="single單例模式\nmultiple多選模式",subtitle_textstyle_opts={"color":"red"}), legend_opts=opts.LegendOpts(selected_mode="multiple"),).render_notebook() )3. 歌曲模塊
3.1. 每首歌的詞云圖
獲取停用詞表
def get_stopword():"使用集合來獲取停用詞表的詞組"s = set()with open(r"./百度停用詞表.txt", encoding="utf-8") as f:for line in f:s.add(line.strip()) # 去掉每行末尾的換行符return s獲取文本的詞頻
def GetWordFrequency(content:str,pattern=None,mode="sub",min_length = 2,min_app=2)->dict:"""content: strpattern: str default [\W]+mode: str `sub` or `findall` min_length: shortest length of wordmin_app: word mininum appearance times--------------------------some sybolm: [,!\"#, -. : ; <=>^_`~!,。?、¥… ():【《》‘’“”\s]+chinese : [\u4e00-\u9fa5]+"""if not pattern:pattern = "[\W]+" # 非中文,字母 和 空格的就替換re_obj = re.compile(pattern)# 預編譯,減少重復匹配text = Noneif mode=="sub":text = re_obj.sub("", content) #正則表達式替換else:text = "".join(re.findall(re_obj,content))wordList = jieba.cut(text) # 分詞s = get_stopword() # 獲取停用詞集合# 字符集的最短長度為2,去除 類似啊啊啊的彈幕內容min_length = 2word_dict = {}for i in wordList:if (len(set(i))>=min_length) and (i not in s):word_dict[i] = word_dict.get(i,0) + 1result = {}for k,v in word_dict.items():if v>=min_app:result[k] = vreturn result獲取詞云圖
def GetWordCloud(word_dict,title="WordCloud")->WordCloud:"""word_dict: dict or listmin_app: word mininum appearance timesmin_length: shortest length of word"""if isinstance(word_dict,dict):word_dict = [(i,j) for i,j in word_dict.items()]wc = (WordCloud().add("", word_dict, word_size_range=[20, 100], shape=SymbolType.DIAMOND).set_global_opts(title_opts=opts.TitleOpts(title=title)))return wc song = df[["發表時刻","顏色","時刻","發送者ID","歌名","內容"]] songName = "七里香" songContentSeris = song.groupby(["歌名"]).get_group(songName)["內容"] songContentStr = "".join(songContentSeris.tolist()) # 只選出詞頻至少為50的中文字詞 freq = GetWordFrequency(songContentStr,pattern="[\u4e00-\u9fa5]+",mode="findall",min_app=50) GetWordCloud(freq,songName).render_notebook()3.2. 歌曲的彈幕顏色分布
song[“十六進制顏色”] = song[“顏色”].astype(“int”).apply(lambda x:"#%06x"%x) 但是Pie內部好像不能識別不了這種格式的顏色
將float型的顏色轉成int型,再根據需要轉成rgb或者十六進制顏色
餅圖
def ColorPie(data,title,min_times=100,bg_color="#d9d9d9"):"data:Series"Data = data[(data>min_times)]colorIndex = Data.index.tolist()colorValue = Data.values.tolist()c = (Pie(init_opts=opts.InitOpts(bg_color=bg_color)).add("", [list(z) for z in zip(colorIndex,colorValue)],).set_colors(colorIndex).set_series_opts(label_opts=opts.LabelOpts( formatter="{c} (ozvdkddzhkzd%)",is_show=True)).set_global_opts(title_opts=opts.TitleOpts(title=title),legend_opts=opts.LegendOpts(type_="scroll",pos_left="80%",orient="vertical"),))return c關閉餅圖的白色圖例后
說明七里香的彈幕顏色主要是紅色 黃色 和 粉色
再來看看 一路向北的
真 一綠向北
3.3. 歌曲的彈幕模式
songName = "七里香" # 彈幕模式映射 modeMap = {"1":"滾動彈幕","2":"滾動彈幕","3":"滾動彈幕","4":"底端彈幕","5":"頂端彈幕","6":"逆向彈幕","7":"精準定位","8":"高級彈幕"} songMode = song.groupby(["歌名"]).get_group(songName)["彈幕模式"].map(modeMap).value_counts() GetPieFig(songMode,songName+"彈幕模式").render_notebook()輸出:
3.4. 歌曲的彈幕發表時刻
def GetLineFig(data,title:str,subtitle:str,series_name:str,):"""data: Series of Datatitle : figure main titlesubtitle: figure second titleseries_name: figure series name"""data.sort_index(inplace=True)x1 = [str(i) for i in data.index.tolist()]y1 = data.values.tolist()c11 = (Line(init_opts=opts.InitOpts(width="600px", height="400px")).add_xaxis(xaxis_data=x1,).add_yaxis(series_name=series_name,y_axis=y1,markpoint_opts=opts.MarkPointOpts(data=[opts.MarkPointItem(type_="max", name="最大值"),opts.MarkPointItem(type_="min", name="最小值"),]),markline_opts=opts.MarkLineOpts(data=[opts.MarkLineItem(type_="average", name="平均值")]),).set_global_opts(title_opts=opts.TitleOpts(title=title, subtitle=subtitle),tooltip_opts=opts.TooltipOpts(trigger="axis"),toolbox_opts=opts.ToolboxOpts(is_show=True),xaxis_opts=opts.AxisOpts(type_="category", boundary_gap=False),))return c11時刻向上取整,也可以用ceil函數
song["time"] = song["發表時刻"].apply(lambda x:int(x)+1) songName = "七里香" timeCount = song.groupby(["歌名"]).get_group(songName)["time"].value_counts() GetLineFig(timeCount,songName,"各時刻的彈幕數量變化",songName).render_notebook()結果
查看該時刻的詞云圖
3.5 對彈幕高峰時刻查看彈幕模式
由上圖,將鼠標移動到一個高峰,看到時刻為84,查看該時刻的彈幕模式
songTime = song.groupby("歌名").get_group(songName) timePoint = 84 modeMap = {"1":"滾動彈幕","2":"滾動彈幕","3":"滾動彈幕","4":"底端彈幕","5":"頂端彈幕","6":"逆向彈幕","7":"精準定位","8":"高級彈幕"}d1 = songTime[songTime["time"]==timePoint]["彈幕模式"].map(modeMap).value_counts().to_dict() GetPieFig(d1,f"{songName}在{timePoint}秒時刻的彈幕模式詞云圖").render_notebook()
視頻中的彈幕是這樣的
3.6 對彈幕高峰時刻查看內容詞云圖
看看詞云圖
songTime = song.groupby("歌名").get_group(songName) timePoint = 84 songContent = "".join(songTime[songTime["time"]==timePoint]["內容"].tolist()) word_dict = GetWordFrequency(songContent,"[\u4e00-\u9fa5]+","findall",min_app=1) GetWordCloud(word_dict,f"{songName}在{timePoint}秒時刻的詞云圖").render_notebook()3.7 對彈幕高峰時刻查看顏色圖
再看顏色占比
songTime = song.groupby("歌名").get_group(songName) song.loc[:,"rgb"] = song.loc[:,"顏色"].astype("int").apply(lambda x:f"rgb({x>>16},{(x>>8)&255},{x&255})") timePoint = 84 singleSongStamp = songTime[songTime["time"]==timePoint]["rgb"].value_counts() ColorPie(singleSongStamp,songName,min_times=1).render_notebook()4. 用戶模塊
4.1 用戶發送的彈幕總數
user = df[["發表時刻","顏色","彈幕模式","時刻","發送者ID","歌名","內容"]] user["發送者ID"].value_counts().head(5)4.2.用戶最常發表的彈幕關鍵詞
userID = "750ff223" userDanmu = user.groupby(["發送者ID"]).get_group(userID)["內容"].tolist() user_dict = GetWordFrequency("".join(userDanmu),"[\u4e00-\u9fa5]+","findall",min_app=10) GetWordCloud(user_dict,title=userID+"的彈幕關鍵詞").render_notebook()結果:
4.3 用戶最愛發彈幕的前N首歌曲
N = 10 # plot版: 適用于簡單查看 user.groupby(["發送者ID"]).get_group(userID)["歌名"].value_counts().head(N).plot(kind="pie") # pyecharts版本 userFav = user.groupby(["發送者ID"]).get_group(userID)["歌名"].value_counts().head(N).to_dict() GetPieFig(userFav,f"{userID}最愛發的彈幕{N}首歌").render_notebook()4.4. 用戶發送彈幕的時間
user["hour"] = pd.to_datetime(user["時刻"],unit="s").dt.hour # 轉換時間格式 user.head() userTime = user.groupby(["發送者ID"]).get_group(userID)["hour"].value_counts() GetLineFig(userTime,f"{userID}發送彈幕時間",None,userID).render_notebook()
這娃絕對是愛修仙的,讓我們看看下一個選手
嗯嗯,這個娃修仙的道行低了點
4.5. 用戶發送的顏色占比
user.loc[:,"顏色"] =user.loc[:,"顏色"].astype("int")# 自身替換 user.loc[:,"rgb"] = user.loc[:,"顏色"].apply(lambda x:f"rgb({x>>16},{(x>>8)&255},{x&255})") userColor = user.groupby("發送者ID").get_group(userID)["rgb"].value_counts() ColorPie(userColor,title=f"{userID}彈幕顏色占比",min_times=1,bg_color="gray").render_notebook()
此用戶最常用的顏色是粉色和白色
4.6 統計包含關鍵詞的次數(一個用戶在單首歌只統計一次)
4.7 統計包含關鍵詞的次數(一個用戶在單首歌統計多次)
group = user[user["內容"].str.contains(keyword)].groupby(["歌名"]) keyword_dict = {} min_times = 100 for name in group.groups.keys():size = group.get_group(name)["發送者ID"].sizeif size>=min_times:keyword_dict[name] = size GetPieFig(keyword_dict,f"{keyword}").render_notebook()
完結
后期會嘗試聯系一些模型,如RFM模型,回歸分析等
總結
以上是生活随笔為你收集整理的pandas数据分析和pyecharts可视化周杰伦MV弹幕(多图长文)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电源检测工具OCCT v9.0.4,小巧
- 下一篇: 迈向“超人认知”:脑机接口的未来