简介
本文将要整理 (以及分析和可视化) 的数据集是推特用户 @dog_rates 的档案, 推特昵称为 WeRateDogs。WeRateDogs 是一个推特主,他以诙谐幽默的方式对人们的宠物狗评分。这些评分通常以 10 作为分母。但是分子则一般大于 10:11/10、12/10、13/10 等等。为什么会有这样的评分?因为 "They're good dogs Brent." WeRateDogs 拥有四百多万关注者,曾受到国际媒体的报道。
收集
获取WeRateDogs Twitter数据集
# WeRateDogs Twitter archive Dataframe
twitter_archive = pd.read_csv('twitter-archive-enhanced.csv')
下载并读取图像预测结果数据集
# Download image-predictions.tsv
url = "https://raw.githubusercontent.com/udacity/new-dand-advanced-china/master/%E6%95%B0%E6%8D%AE%E6%B8%85%E6%B4%97/WeRateDogs%E9%A1%B9%E7%9B%AE/image-predictions.tsv"
urlretrieve(url, 'image-predictions.tsv')
# image predictions Dataframe
image_predict = pd.read_csv('image-predictions.tsv', sep='\t')
获取转发数等额外数据
# Twitter extra data
extra_list = []
with open('tweet_json.txt', 'r') as file:
for line in file.readlines():
jsonObj = json.loads(line)
extra_list.append({'tweet_id': jsonObj['id'], 'retweet_count': jsonObj['retweet_count'], 'favorite_count':jsonObj['favorite_count']})
extra_data = pd.DataFrame(extra_list)
评估
观察评估
随机抽取5条推特样本数据观察
twitter_archive.sample(5)
各列数据含义
列名 | 含义 |
---|---|
tweet_id | 档案中的推特 ID |
in_reply_to_status_id | 回复ID |
in_reply_to_user_id | 被回复推文原始用户ID |
timestamp | 发文时间 |
source | 消息来源(使用设备) |
text | 推文内容 |
retweeted_status_id | 转发ID |
retweeted_status_user_id | 转发用户ID |
retweeted_status_timestamp | 转发时间 |
expanded_urls | 推文链接 |
rating_numerator | 评分分子 |
rating_denominator | 评分分母 |
name | 宠物名 |
doggo | 狗的成长阶段,分类变量 |
floofer | 狗的成长阶段,分类变量 |
pupper | 狗的成长阶段,分类变量 |
puppo | 狗的成长阶段,分类变量 |
通过观察数据,发现一些问题:
质量
- column ['in_reply_to_status_id', 'in_reply_to_user_id', 'retweeted_status_id', 'retweeted_status_user_id', 'retweeted_status_timestamp', 'doggo', 'floofer', 'pupper', 'puppo'] 缺少数据
-
timestamp
后面多了+0000
整洁度
timestamp
应该拆分成两列date
和time
image_predict.sample(5)
随机抽取5条图像预测数据观察。各列数据含义如下。
列名 | 含义 |
---|---|
tweet_id | 档案中的推特 ID |
jpg_url | 预测的图像资源链接 |
img_num | 最可信的预测结果对应的图像编号 |
p1 | 算法对推特中图片的一号预测 |
p1_conf | 算法的一号预测的可信度 |
p1_dog | 一号预测该图片是否属于“狗”(有可能是其他物种,比如熊、马等) |
p2 | 算法对推特中图片预测的第二种可能性 |
p2_conf | 算法的二号预测的可信度 |
p2_dog | 二号预测该图片是否属于“狗” |
p3 | 算法对推特中图片预测的第三种可能性 |
p3_conf | 算法的三号预测的可信度 |
p3_dog | 三号预测该图片是否属于“狗” |
extra_data.sample(5)
随机抽取5条转发数等额外数据观察。各列数据含义如下:
列名 | 含义 |
---|---|
tweet_id | 档案中的推特 ID |
favorite_count | 点赞数 |
retweet_count | 转发数 |
编程评估
twitter_archive.info()
-
timestamp
应该为datetime
类型 - 数据中包含了转发的数据(
retweeted_status_id
非空)
twitter_archive.describe()
rating_numerator和rating_denominator存在异常大的值,且rating_numerator标准差较大。两者需要进一步观察。
twitter_archive.rating_numerator.value_counts()
twitter_archive.rating_denominator.value_counts()
部分异常数值:
420 2
666 1
960 1
1776 1
Name: rating_numerator, dtype: int64
110 1
120 1
130 1
150 1
170 1
Name: rating_denominator, dtype: int64
-
rating_numerator
和rating_denominator
有三位数和四位数,且数据类型为int
- 考虑增加一列
rating
存放
twitter_archive.source.value_counts()
输出结果:
<a href="http://twitter.com/download/iphone" rel="nofollow">Twitter for iPhone</a> 2221
<a href="http://vine.co" rel="nofollow">Vine - Make a Scene</a> 91
<a href="http://twitter.com" rel="nofollow">Twitter Web Client</a> 33
<a href="https://about.twitter.com/products/tweetdeck" rel="nofollow">TweetDeck</a> 11
-
source
不需要标签和链接等,保留文本内容即可
twitter_archive.name.value_counts()
部分输出结果:
None 745
a 55
Charlie 12
Lucy 11
Cooper 11
Oliver 11
Lola 10
Penny 10
Tucker 10
Winston 9
Bo 9
the 8
Sadie 8
Bailey 7
Daisy 7
Toby 7
Buddy 7
an 7
-
name
缺少部分数据,且有一些名字为a
,an
,the
等 - 缺失值填充为
None
,没有被当成空值
image_predict.info()
image_predict.describe()
p1预测可信度最高
#查看是否存在重复预测的图像
sum(image_predict.jpg_url.duplicated())
结果为66。
- 存在重复预测的图像
extra_data.info()
all_columns = pd.Series(list(twitter_archive) + list(image_predict) + list(extra_data))
all_columns[all_columns.duplicated()]
评估结果
质量
twitter_archive
table
- column ['in_reply_to_status_id', 'in_reply_to_user_id', 'retweeted_status_id', 'retweeted_status_user_id', 'retweeted_status_timestamp', 'doggo', 'floofer', 'pupper', 'puppo'] 缺少数据
-
timestamp
后面多了+0000
-
timestamp
应该为datetime
类型 - 数据中包含了转发的数据(
retweeted_status_id
非空) -
rating_numerator
和rating_denominator
有三位数和四位数,且数据类型为int
- 考虑增加一列
rating
存放 -
source
不需要标签和链接等,保留文本内容即可 -
name
缺少部分数据,且有一些名字为a
,an
,the
等 - 缺失值填充为
None
,没有被当成空值
整洁度
-
twitter_archive
表中的timestamp
应该拆分成两列date
和time
-
image_predict
存在重复预测的图像记录 -
extra_data
的favorite_count
和retweet_count
应合入twitter_archive
表格中 - 将
image_predict
和twitter_archive
合并
清理
# 备份
twitter_archive_clean = twitter_archive.copy()
image_predict_clean = image_predict.copy()
数据清理每一项都有固定的步骤:定义-代码-测试
- 定义说明数据存在的问题项,以及该如何解决
- 代码即根据定义给出的解决方法,进行编写代码
- 测试即测试是否解决问题
下面除了第一项给出完整步骤作为例子,后面为了缩减文件幅度,不给出测试步骤
定义
['in_reply_to_status_id', 'in_reply_to_user_id', 'retweeted_status_id', 'retweeted_status_user_id', 'retweeted_status_timestamp'] 缺少数据且不需要用到分析中,直接用drop
丢弃掉。['retweeted_status_id', 'doggo', 'floofer', 'pupper', 'puppo'] 暂时保留
代码
twitter_archive_clean = twitter_archive_clean.drop(['in_reply_to_status_id', 'in_reply_to_user_id', \
'retweeted_status_user_id', 'retweeted_status_timestamp'], axis=1)
测试
定义
timestamp
后面多了+0000
用切片舍弃+0000
代码
twitter_archive_clean['timestamp'] = twitter_archive_clean.timestamp.str[:-5]
定义
twitter_archive
表中的timestamp
应该拆分成两列date
和time
用str.split
拆分开,然后用drop
删除timestamp
列
代码
twitter_archive_clean['timestamp'] = twitter_archive_clean.timestamp.str.strip()
twitter_archive_clean['date'], twitter_archive_clean['time'] = twitter_archive_clean.timestamp.str.split(" ").str
twitter_archive_clean.drop('timestamp', axis=1, inplace=True)
定义
date
应该为datetime类型
用to_datetime
转换成datetime
类型
代码
twitter_archive_clean['date'] = pd.to_datetime(twitter_archive_clean['date'])
定义
数据中包含了转发的数据(retweeted_status_id非空)
过滤retweeted_status_id
非空的行,然后丢弃retweeted_status_id
代码
twitter_archive_clean = twitter_archive_clean[twitter_archive_clean['retweeted_status_id'].isnull()]
twitter_archive_clean.drop('retweeted_status_id', axis=1, inplace=True)
定义
rating_numerator
和rating_denominator
有三位数和四位数,且数据类型为int
将rating_numerator
和rating_denominator
转换成正确的数字,且数据类型改为float
代码
twitter_archive_clean.rating_numerator.astype('float64')
twitter_archive_clean.rating_denominator.astype('float64')
numBetw20_200 = (twitter_archive_clean['rating_numerator'] > 20) & (twitter_archive_clean['rating_numerator'] <= 200)
numBeyond200 = twitter_archive_clean['rating_numerator'] > 200
denoBeyond20 = twitter_archive_clean['rating_denominator'] > 20
twitter_archive_clean.loc[numBetw20_200, 'rating_numerator'] = twitter_archive_clean.loc[numBetw20_200, 'rating_numerator'] / 10.0
twitter_archive_clean.loc[numBeyond200, 'rating_numerator'] = twitter_archive_clean.loc[numBeyond200, 'rating_numerator'] / 100.0
twitter_archive_clean.loc[denoBeyond20, 'rating_denominator'] = twitter_archive_clean.loc[denoBeyond20, 'rating_denominator'] / 10.0
定义
增加一列rating
存放
计算并存到新列rating
代码
twitter_archive_clean['rating'] = twitter_archive_clean['rating_numerator']/twitter_archive_clean['rating_denominator']
定义
source
不需要标签和链接等,保留文本即可
用str.extract
提取
代码
twitter_archive_clean['source'] = twitter_archive_clean.source.str.extract(r'(?<=\>)(.*?)(?=\<)',expand=True)
定义
name
缺少部分数据,且有一些名字为a
,an
,the
等
用replace
将a
,an
等不是名字的单词替换成np.NaN
代码
twitter_archive_clean['name'] = twitter_archive_clean['name'].replace(r'a|an|the|such', np.NaN,regex=True)
定义
缺失值填充为None
,没有被当成空值
用replace方法将所有None
替换成np.NaN
代码
twitter_archive_clean = twitter_archive_clean.replace('None', np.NaN)
定义
image_predict
存在重复预测的图像记录,需要删除重复记录
代码
image_predict_clean = image_predict_clean[~image_predict_clean.jpg_url.duplicated()]
定义
extra_data
的favorite_count
和retweet_count
应合入twitter_archive
表格中
用merge
将两表合并
代码
twitter_archive_clean = pd.merge(twitter_archive_clean, extra_data, on='tweet_id', how='left')
定义
将image_predict
和twitter_archive
合并
用merge
将两表合并
代码
twitter_archive_clean = pd.merge(twitter_archive_clean, image_predict_clean, on='tweet_id', how='inner')
到这里,第一阶段数据清洗完毕。
再评估
- 关于狗的地位的信息只有317条,远小于总条数
-
name
缺失过多数据 -
source
应该为category
类型
再清洗
定义
关于狗的地位的信息只有317条,远小于总条数
从text
中提取狗的地位存放到stage
,然后删掉4列狗的地位
代码
twitter_archive_clean['status'] = twitter_archive_clean.text.str.findall('doggo|pupper|puppo|floofer|blep|snoot|floof').apply(lambda x : " ".join(set(x)))
twitter_archive_clean.drop(['doggo','puppo','pupper','floofer'], axis=1, inplace=True)
定义
name
缺失过多数据
从text
中提取狗的名字
代码
twitter_archive_clean['name'] =
twitter_archive_clean.text.str.extract(r'(?:is\s|Meet\s|to\s)([A-Z][a-z]*)', expand=True)
定义
source
应该为category
类型
用astype
将source
的数据类型换成category
代码
twitter_archive_clean['source'] = twitter_archive_clean.source.astype('category')
定义
将狗的地位为空的记录替换成np.NaN
代码
twitter_archive_clean['status'] = twitter_archive_clean['status'].apply(lambda x: np.NaN if len(str(x)) == 0 else x)
清洗完毕后,保存数据。
# 保存清洗后的数据
twitter_archive_clean.to_csv('twitter_archive_master.csv', index=None)
可视化分析
import matplotlib.pyplot as plt
%matplotlib inline
# 读取清洗后的数据
twitter_archive_master = pd.read_csv('twitter_archive_master.csv')
plt.figure(figsize=(13,5))
plt.subplot(1,2,1)
plt.hist(twitter_archive_master.source)
plt.title('Twitter Data Source Count')
plt.ylabel('Count')
plt.xlabel('Source')
plt.subplot(1,2,2)
plt.pie(twitter_archive_master.source.value_counts(),labels=twitter_archive_master.source.value_counts().index ,autopct='%3.1f %%')
plt.axis('equal')
plt.show()
从上面两幅图可以看出,数据绝大多数(98%)来源于
Twitter for iPhone
(手机客户端),只有小部分来源于桌面应用和浏览器。显而易见的,多数人比较喜欢用Twitter手机客户端来浏览Twitter。
plt.hist(twitter_archive_master.time.str[:2],bins=24, facecolor='g')
plt.grid(True)
plt.title('Period of Comment')
plt.ylabel('Count of Comments')
plt.xlabel('Time(h)')
plt.show()
从用户评论时间来看,最高峰是凌晨一点钟,其次是凌晨十二点,然后是凌晨两点。结合前面数据98%来源于手机客户端,可以看出用户都有睡前玩手机浏览Twitter的习惯。
plt.boxplot(twitter_archive_master.rating, vert=False, showfliers=False)
plt.title('Rating Boxplot Without Outliers')
plt.show()
关于狗的评分平均值是1.05,中位值1.1。也即是说分子评级高于分母评级。并且评分高于1的占79%左右。
import seaborn as sns
sns.set_style("white")
sns.set(style="darkgrid")
sns.lmplot(x='retweet_count', y='favorite_count',data = twitter_archive_master)
plt.title('retweet_count VS favorite_count')
plt.show()
转发数与点赞数具有较强的正相关性。其实这也不难得出,毕竟转发或者点赞都是因为喜欢这条tweet才做出的行为。而且转发量大,tweet被人看到的机会也会更大,那么会被点赞的几率也就更大了。
p1预测比较准确,接下来就用p1来筛选出狗的数据,然后分析哪种类型的宠物狗比较受欢迎。
# 根据p1的预测结果筛选宠物狗的品种
df_dogs = twitter_archive_master[['predict1', 'predict1_dog', 'rating', 'favorite_count']]
df_dogs = df_dogs[df_dogs.predict1_dog == True]
# 根据品种分组,再进行聚合计算,按点赞数从多到少排序
dogs_summary = df_dogs.groupby('predict1').agg({'rating':'mean', 'favorite_count':'sum'})\
.sort_values(by = 'favorite_count', ascending = False)
# 筛选点赞数最多的前10种宠物狗
Top10Dogs = dogs_summary.head(10)
# 看favorite_count与rating两者是否相关
sns.set(style="darkgrid")
sns.lmplot(x='favorite_count', y='rating',data = dogs_summary)
plt.title('favorite_count VS rating')
plt.show()
可以看出
favorite_count
与rating
两者相关性很弱。相关系数小于0.3,可以视为无线性相关。
Top10Dogs['favorite_count'].plot(kind = 'bar')
可以看到获得点赞数最多的宠物狗类型是golden_retriever,也就是金毛啦。性格温顺,且比较体贴的金毛当然比较受欢迎,我也喜欢金毛。
反思
在数据清洗时,对一些异常数据进行处理,例如评分,可能对后面的分析造成了影响,例如可能影响到了后面得出评分与点赞数无相关的结论。后续应该多思考,如何进行数据清洗可以使得结论更加准确,而不用担心得出错误的结论。
网友评论