手把手教你完成一個資料科學小專案(3):資料異常與清洗

古柳_Deserts_X發表於2018-08-16

前言

本系列將全面涉及本專案從爬蟲、資料提取與準備、資料異常發現與清洗、分析與視覺化等細節,並將程式碼統一開源在GitHub:DesertsX/gulius-projects ,感興趣的朋友可以先行 star 哈。

請先閱讀“中國年輕人正帶領國家走向危機”,這鍋背是不背? 一文,以對“手把手教你完成一個資料科學小專案”系列有個全域性性的瞭解。

截止目前,我們已經通過(1)資料爬取(2)資料提取、IP查詢,分別完成了對新浪財經《中國年輕人正帶領國家走向危機》一文評論資料的爬取和資料的提取。如何你沒看前兩個教程、也沒有一步步執行和理解之前的程式碼,“不要慌”,直接拿Sina_Finance_Comments_All_20180811.csv 資料進行分析、挖掘和視覺化就行。

資料讀取

本文繼續用 Python 的 pandas 等資料科學庫完成所有操作。首先讀取資料,每一行代表一條評論,每一列代表每一條評論裡的某一維度資料。很多列最後也沒用上,但最開始並不知道,所以先都讀取進來,不做篩選。

import pandas as pd
df = pd.read_csv('Sina_Finance_Comments_All_20180811.csv',encoding='utf-8')
df.head()
複製程式碼

評論數

首先來看下所有評論數隨時間的變化情況。

建立時間戳列

由日期列建立出對應的時間戳列。

from datetime import datetime
def time2stamp(cmnttime):
    cmnttime = datetime.strptime(cmnttime, '%Y-%m-%d %H:%M:%S') 
    stamp = int(datetime.timestamp(cmnttime))
    return stamp
df['stamp'] = df['time'].apply(time2stamp)
df.head()
複製程式碼

DataFrame 的 shape 代表行數(爬到的評論總數)與列數:

df.shape
複製程式碼
(3795, 19)
複製程式碼

建立評論數計數列

根據評論時間的前後,建立評論數計數列,即最早一條評論記為1,後續遞增,最後一條也就是評論總數。

import matplotlib.pyplot as plt
%matplotlib inline
df['cmntcount'] =int(df.shape[0])-df['No']
df['cmntcount'].head()
複製程式碼

計數順序和索引順序正好相反:

0    3794
1    3793
2    3792
3    3791
4    3790
Name: cmntcount, dtype: int64
複製程式碼

資料異常

評論數隨時間戳的變化曲線有異常,一開始沒太在意那一水平線是什麼情況所致,也不知道哪裡出的有問題,所以就先忽略了,繼續後續的探索分析:

plt.plot(df.stamp, df.cmntcount);
複製程式碼

pyecharts 之評論數變化曲線

本專案將多次使用 pyecharts 進行資料視覺化。大家也可以自行安裝 pip install pyecharts ,並按照官方文件:pyecharts 圖表配置 進行學習和使用。

具體支援的圖表羅列如下:

  • Bar(柱狀圖/條形圖)/ Bar3D(3D 柱狀圖)/ Boxplot(箱形圖)/ EffectScatter(帶有漣漪特效動畫的散點圖)/ Funnel(漏斗圖)
  • Gauge(儀表盤)/ Geo(地理座標系)/ Graph(關係圖)/ HeatMap(熱力圖)/ Kline(K線圖)/ Line(折線/面積圖)/ Line3D(3D 折線圖)
  • Liquid(水球圖)/ Map(地圖)/ Parallel(平行座標系)/ Pie(餅圖)/ Polar(極座標系)/ Radar(雷達圖)/ Sankey(桑基圖)
  • Scatter(散點圖)/ Scatter3D(3D 散點圖)/ ThemeRiver(主題河流圖)/ WordCloud(詞雲圖)

注:開源後的 jupyter notebook裡 pyecharts 圖表部分無法顯示,需 download 後執行程式碼過才可檢視。

每小時評論陣列合圖

由於本文為了引出資料中存在異常,所以跳過 notebook 裡的折線圖和柱形圖單圖,直接拿最後的組合圖(pyecharts 配置文件 overlap)進行說明。

擷取時間列拿到月份日期和小時,並根據每小時進行分組統計:

from pyecharts import Bar, Line, Overlap
df['time_mdh'] = df.time.apply(lambda x:x.split(':')[0][5:])
df_mdhmax = df.groupby('time_mdh')['cmntcount'].max()
df_mdhcount = df.groupby('time_mdh')['cmntcount'].count()
from pyecharts import Bar, Line, Overlap
bar = Bar("每小時評論數")
bar.add("小時", df_mdhcount.index, df_mdhcount.values,is_label_show=True,xaxis_interval=-90,
        xaxis_rotate=-90, yaxis_interval=200,yaxis_max=800)
line = Line("每小時評論數")
line.add("小時", df_mdhmax.index, df_mdhmax.values,line_opacity=1,
         line_type='dotted', yaxis_interval=1000,yaxis_max=4000)

overlap = Overlap()
overlap.add(bar)
overlap.add(line, is_add_yaxis=True, yaxis_index=1)
#overlap.render() # 使用 render() 渲染生成 .html 檔案
overlap
複製程式碼

可以看到組合圖裡,柱形圖似乎沒什麼問題,最早的評論出現在8月7號的晚上8點,最大的高峰出現在在8月8號上午9點,單小時評論數高達659條,之後逐漸衰減;


而曲線圖裡8月9號上午8點至9點兩個時間點的累積評論數超過了相鄰的前後時間段。凸起的部分不得不令人懷疑之前拿到的資料是有問題的,難道千辛萬苦用爬蟲拿到的資料出了么蛾子?!

異常檢測

不過既然知道了異常可能就在2018-08-09 8點-9點,那就選擇這倆時間點的資料進行下排查下,一行程式碼就行:

df[df.time_mdh.str.contains('08-09 08')]
複製程式碼

發生評論資料有重複,並且在表格中的資料並沒有如設想的那樣按照時間先後排列。

df[df.time_mdh.str.contains('08-09 09')]
複製程式碼

9點的評論同樣有重複,不方便顯示就不放了。一開始也不清楚該問題為什麼會發生,感覺爬蟲部分沒有問題,提取資料也中規中矩,後來重新爬取資料時發現,頁碼數在總頁數的前幾頁就停止了。

至於重複是如何產生的,也是未解之謎,有知道的小小夥伴可以留言告訴我哈。

不過雖然不知道異常究竟如何產生的,但去除異常資料的方式卻可由去重並重新設定下 index 索引和重設評論數計數列等實現。

資料清洗

由於本文一開始的資料就存在異常,所以“一朝回到解放前”,讓我們重新讀取資料,一切從頭開始,首先就是刪除掉重複的行:


將使用者暱稱和評論內容均一致的行刪除重複,輸出前後 shape 的變化後:

df = pd.read_csv('Sina_Finance_Comments_All_20180811.csv',encoding='utf-8')
print(df.shape)
df.drop_duplicates(subset=['nick', 'content'], keep='first',inplace=True)
print(df.shape)
複製程式碼

共刪除22行:

(3795, 22)
(3773, 22)
複製程式碼

建立新的時間列

from datetime import datetime
def time2stamp(cmnttime):
    cmnttime = datetime.strptime(cmnttime, '%Y-%m-%d %H:%M:%S') 
    stamp = int(datetime.timestamp(cmnttime))
    return stamp
df['stamp'] = df['time'].apply(time2stamp)
df['time_ymd'] = df.time.apply(lambda x:x.split(' ')[0]) # 年月日
df['time_mdh'] = df.time.apply(lambda x:x.split(':')[0][5:]) #月日時 # 方便後續視覺化時橫座標展示
df.head()
複製程式碼

按時間排序後重置 index 索引
pandas.DataFrame.sort_values
pandas.DataFrame.reset_index

df.sort_values(by=["stamp"],ascending=False,inplace=True)
df.reset_index(inplace=True,drop=True)
複製程式碼

建立評論數計數列後,將資料儲存到新的csv裡,後續就可以只在新csv裡操作,而不必每次重新清洗資料了:

import matplotlib.pyplot as plt
%matplotlib inline
df['cmntcount'] =int(df.shape[0])-df.index
df.to_csv('Sina_Finance_Comments_All_20180811_Cleaned.csv', encoding='utf-8', line_terminator='\r\n')
複製程式碼

最後組合圖的評論數變化情況也正常了。

小結

本次遇到資料裡出現異常也是始料不及,想當然的設想資料格式準確並去進行分析和視覺化的結果就是一頓操作後,發現不得不掉頭解決掉異常,於是很多努力“一朝回到解放前”,但這可能就是人生吧,那有什麼一帆風順,人生不就是起落落落落落落落落落嘛!逃。

本系列將全面涉及本專案從爬蟲、資料提取與準備、資料異常發現與清洗、分析與視覺化等細節,並將程式碼統一開源在GitHub:DesertsX/gulius-projects ,感興趣的朋友可以先行 star 哈。


相關文章