狗狗评分分析

一、项目简介

(一)项目情况

推特用户 @dog_rates 的档案也叫做 WeRateDogs。以诙谐幽默的方式对人们的宠物狗评级。这些评级通常以 10 作为分母。但是分子呢?分子一般大于 10。如11/10、12/10、13/10 等,WeRateDogs 拥有四百多万关注者,曾受到国际媒体的报道。WeRateDogs 推特档案包括基本的推特信息,如 5000 多条推特,但并不包括所有数据,转发用户和喜爱用户是两个遗漏的列。不过档案中有一列包括每个推特文本,可以用来提取评级、狗的名字和 "地位" (即 doggo、floofer、pupper 和 puppo)。

tweet_json文件为 txt 格式,每一行为一条独立的 twitter 信息,格式为 JSON ,其中有转发和喜欢。

通过一个 神经网络 运行 WeRateDogs 推特档案中的所有图片,这个神经网络可以对狗的品种分类。结果:对图片预测 (只含前三名) 的文件包括每个推特 ID、图片 URL 和最自信预测对应的图片编号 (由于推特最多包含 4 个图片,所以编号为 1 到 4)。

(二)数据情况

twitter-archive-enhanced.csv,推特档案

tweet_json.txt,含转发和喜欢

image-predictions.tsv,根据推特档案里的图片预测的狗

1、twitter-archive-enhanced.csv,推特档案

字段含义:
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:狗的成长阶段,分类变量

 2、tweet_json.txt,含转发和喜欢

取一行在解析后的情况如下:

字段含义:
retweet_count: 转发数 
favorite_count: 喜爱数

3、image-predictions.tsv,根据推特档案里的图片预测的狗

字段含义:
tweet_id:档案中的推特 ID
jpg_url:预测的图像资源链接
img_num:最可信的预测结果对应的图像编号
p1:算法对推特中图片的一号预测
p1_conf:算法的一号预测的可信度
p1_dog:一号预测该图片是否属于“狗”(有可能是其他物种,比如熊、马等)
p2:算法对推特中图片预测的第二种可能性
p2_conf:算法的二号预测的可信度
p2_dog:二号预测该图片是否属于“狗”
p3:算法对推特中图片预测的第三种可能性
p3_conf:算法的三号预测的可信度
p3_dog:三号预测该图片是否属于“狗”

 (三)项目目的

 收集包含图片但不包含转发内容的原始评级,另外,每条推特的数据,至少要包含转发数(retweet_count)和喜欢数(favorite_count)。最终对收集的数据进行评估清洗,并对清洗过的数据进行储存、分析和可视化。

二、数据处理

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

plt.style.use('ggplot')
%matplotlib inline

   一) 导入数据

# 1、Read the twitter-archive-enhanced dataset 
df_twitter = pd.read_csv(r'C:\Users\...\Desktop\python\Python Project\0_重新深入探索\Data\twitter-archive-enhanced.csv')

# 2、Read the image_predictions file
df_image = pd.read_csv(r'C:\Users\...\Desktop\python\Python Project\0_重新深入探索\Data\image-predictions.tsv', sep='\t')

# 3、Read the tweet_json file
import json
df_json = pd.DataFrame(columns = ['tweet_id', 'retweet_count', 'favorite_count'])
with open(r'C:\Users\...\Desktop\python\Python Project\0_重新深入探索\Data\tweet-json.txt') as file:
    for line in file:  # 一行一行的读取txt数据并提取需要的数据
        status = json.loads(line)
        tweet_id = status['id_str']
        retweet_count = status['retweet_count']
        favorite_count = status['favorite_count']
        df_json = df_json.append(pd.DataFrame([[tweet_id, retweet_count, favorite_count]], columns=['tweet_id', 'retweet_count', 'favorite_count']))     
df_json = df_json.reset_index(drop=True)

  二)数据清洗

# Copy three dataframes first
df_twitter_clean = df_twitter.copy()
df_image_clean = df_image.copy()
df_json_clean = df_json.copy()

 (一) df_json_clean

df_json_clean.head(2)
df_json_clean.info()
df_json_clean.describe(include=["O"])

 总结: 没有缺失值,tweet_id没有重复值,所以只需要改变数据的类型

df_json_clean['tweet_id'] = df_json_clean['tweet_id'].astype('str')
df_json_clean['retweet_count'] = df_json_clean.retweet_count.astype('int')
df_json_clean['favorite_count'] = df_json_clean.favorite_count.astype('int')
# recheck
df_json_clean.info()

(二) df_image_clean

pd.set_option('display.max_columns',100)  #显示100列
pd.set_option('display.max_rows',None)  # 显示所有行(参数设置为None代表显示所有行,也可以自行设置数字)
pd.set_option('max_colwidth',200)  # 设置数据的显示宽度,默认为50 (大于字段宽度则自适应宽度)
pd.set_option('expand_frame_repr', True)  #禁止自动换行(设置为Flase不自动换行,True反之)

df_image_clean.head(2)
df_image_clean.info()

df_image_clean['tweet_id'].value_counts().nlargest(2)
df_image_clean['p1_dog'].value_counts()

 总结:tweet_id没有缺失值;p1_dog表明p1(狗的种类)存在不是狗种类的数据,这里剔除不是狗狗的数据(p1_dog为False);然后需要剔除掉无用列;再转换数据的类型

# 删除不是狗狗的数据
df_image_clean = df_image_clean[df_image_clean['p1_dog'] == True]

# 删除掉多余列
df_image_clean = df_image_clean.drop(columns=['p1_dog','p2','p2_dog','p2_conf','p3','p3_conf','p3_dog']) 

# 将预测出的种类名称全部变成小写
df_image_clean['p1'] = df_image_clean['p1'].map(lambda x: x.lower())

# 修改数据类型
df_image_clean['tweet_id'] = df_image_clean['tweet_id'].astype('str')
df_image_clean['jpg_url'] = df_image_clean['jpg_url'].astype('str')
df_image_clean['img_num'] = df_image_clean['img_num'].astype('int')
df_image_clean['p1'] = df_image_clean['p1'].astype('str')
df_image_clean['p1_conf'] = df_image_clean['p1_conf'].astype('float')

# recheck
df_image_clean.info()

df_image_clean.head(2)  # 处理后的最终结果

 (三) df_twitter_clean

df_twitter_clean.sample(2)

1、总体情况 

df_twitter_clean.info()

plt.figure(figsize=(10,6))
sns.heatmap(df_twitter.isnull(), cbar=False) # 查看各字段缺失值情况,白色越多,缺失值(nan)越多,注意这里None不会按缺失值显示
plt.show()

df_twitter.isnull().sum()  # 看各字段缺失值情况

df_twitter['tweet_id'].value_counts().nlargest(2)

df_twitter[-df_twitter['retweeted_status_id'].isnull()].count()

 总结:

1、有5个字段缺失非常多,因此删除掉;
2、其中由retweeted_status_id可以看到 属于转发别人的推文有181条,为避免重复我们只对原始推文进行分析,即剔除retweeted_status_id不为空的数据;
3、expanded_urls也存在59条缺失值,剔除expanded_urls有缺失的行(即没有照片的数据)
4、同样需要改变字段的数据类型

# 1.1、删除转发的(因为转发别人的可能重复,所以只保留原创)
df_twitter_clean = df_twitter_clean[df_twitter_clean['retweeted_status_id'].isnull() == True]

df_twitter_clean['retweeted_status_id'].value_counts()

# 1.2、剔除expanded_urls有缺失的行
df_twitter_clean = df_twitter_clean[df_twitter_clean['expanded_urls'].isnull() == False]

df_twitter_clean['expanded_urls'].isna().sum()

# 1.3、删除缺失值非常多5列
df_twitter_clean = df_twitter_clean.drop(columns=['in_reply_to_status_id','in_reply_to_user_id','retweeted_status_id','retweeted_status_user_id','retweeted_status_timestamp'])


# 1.4、修改数据类型
df_twitter_clean['tweet_id'] = df_twitter_clean['tweet_id'].astype('str')
df_twitter_clean['timestamp'] = pd.to_datetime(df_twitter_clean['timestamp'])
df_twitter_clean.info()


df_twitter_clean.head(2)

 2、对source进行处理

df_twitter_clean['source'][0]

 总结:我们只需要> 与 之间的数据,即这里的 “Twitter for iPhone”

# 2、用正则提取需要的数据
import re
temp = re.compile('>(.*)</a>')  # 编译正则规则
df_twitter_clean['source'] = df_twitter_clean['source'].map(lambda x: temp.findall(x)[0])

df_twitter_clean['source'].value_counts()

3、对expanded_urls进行处理

df_twitter_clean['expanded_urls'].value_counts().nlargest(5)

df_twitter_clean[df_twitter_clean['expanded_urls'].duplicated()].count()

# 3、查看expanded_urls重复的行
df_twitter_clean[df_twitter_clean['expanded_urls'] == 'https://vine.co/v/ea0OwvPTx9l']

总结:expanded_urls 有一个重复值,但是定位到内容后发现推文描述不一样,故不用进行去重处理

注:去重的方法:df_twitter_clean = df_twitter_clean.drop_duplicates(subset = 'expanded_urls', keep='first')

4、对rating_numerator、rating_denominator进行处理

df_twitter_clean[['rating_numerator','rating_denominator']].describe()

# df_twitter_clean.rating_numerator.hist(figsize=(10,6),bins=10)
# df_twitter_clean.rating_denominator.hist(figsize=(10,6),bins=20)

df_twitter_clean.rating_numerator.value_counts()
df_twitter_clean.rating_denominator.value_counts()

1) 对于rating_denominator

df_twitter_clean[df_twitter_clean.rating_denominator >10].count()

df_twitter_clean[df_twitter_clean.rating_denominator >10]
df_twitter_clean[df_twitter_clean.rating_denominator <10]

可以看到,分母大于10的有16条数据,小于10的有2条数据,经查看发现其分子和分母属于同一数量级的,所以不用剔除;但是看到推文中可能有多个评分的情况

2) 对于rating_numerator

df_twitter_clean.rating_numerator.value_counts()

 过了14就是 24的评分,将大于14的单独查看

df_twitter_clean[df_twitter_clean.rating_numerator > 14]

可以看到评分提取有误,有小数点的数据提取不全,需要重新

# 重新提取评分数据
import re
df_twitter_clean['score_new'] = df_twitter_clean['text'].map(lambda x: re.findall('\d+\.\d+/\d+|\d+/\d+',x))  # 提取有小数点和没有小数点的所有数据
df_twitter_clean['score_new'].value_counts()

 这里也可以看到评分数据有多个的情况,下面查看一些评分有多个的数据情况

# 查看评分有多个的数据情况
df_twitter_clean['score_new_len'] = df_twitter_clean['score_new'].map(lambda x: len(x))
df_twitter_clean['score_new_len'].value_counts()

df_twitter_clean[df_twitter_clean['score_new_len']>1]

当评分数据大于一个时,剔除该行,因为其可能发了多种狗狗的评论,其评分分别对应不同种的狗狗

df_twitter_clean = df_twitter_clean[df_twitter_clean['score_new_len'] == 1]
df_twitter_clean['score_new'].value_counts()
df_twitter_clean.head(2)

df_twitter_clean['score_new_01'] = df_twitter_clean['score_new'].map(lambda x: x[0].split('/')[0]).astype(float)
df_twitter_clean['score_new_02'] = df_twitter_clean['score_new'].map(lambda x: x[0].split('/')[1]).astype(float)

df_twitter_clean['score_new_01'].value_counts()
df_twitter_clean[(df_twitter_clean['score_new_01']>30) & (df_twitter_clean['score_new_02']<=10)]

可以看到分子/分母大于3的只有这两条数据,过于异常,剔除掉

df_twitter_clean = df_twitter_clean[~((df_twitter_clean['score_new_01']>30) & (df_twitter_clean['score_new_02']<=10))]

df_twitter_clean['score_new_01'].value_counts()
df_twitter_clean['score_new_02'].value_counts()
df_twitter_clean[df_twitter_clean.rating_denominator == 10]['score_new_01'].value_counts()

4) 之前与新提取得分的差异

df_twitter_clean[df_twitter_clean['score_new_01'] != df_twitter_clean['rating_numerator']]
df_twitter_clean[df_twitter_clean['score_new_02'] != df_twitter_clean['rating_denominator']]

结果:只有4条数据与之前不一样,即只更新了小数点提取有误的数据,数据处理无误

#替换名称,删除临时字段
df_twitter_clean['rating_numerator'] = df_twitter_clean['score_new_01']
df_twitter_clean['rating_denominator'] = df_twitter_clean['score_new_02']

df_twitter_clean = df_twitter_clean.drop(columns=['score_new_01','score_new_02','score_new','score_new_len'])

df_twitter_clean.head(2)

 5) 再次检查分子分母情况

x = df_twitter_clean[df_twitter_clean['rating_numerator']<= 20.0].rating_numerator
sns.distplot(x, kde=False)
plt.xlabel('Ratings')
plt.ylabel('Count')
plt.title('Distribution of Ratings')
plt.show()

df_twitter_clean['rating_numerator'].value_counts()

 

sns.distplot(df_twitter_clean['rating_denominator'], kde=False, rug=True)
plt.show()

df_twitter_clean['rating_denominator'].describe() 

 

  5、对name进行处理

df_twitter_clean['name'].value_counts()

# 对字段:name进行分析
vals = df_twitter_clean[~df_twitter_clean['name'].str[0].str.isupper()]['name'].value_counts()
vals.keys()

总结:name字段存在不是名字的情况,对这样的数据进行剔除

# 5、把不正确的名字转化成 None
for val in vals.keys():
    df_twitter_clean['name'] = df_twitter_clean['name'].replace(val,'None')

df_twitter_clean[~df_twitter_clean['name'].str[0].str.isupper()]['name'].value_counts() # 检查是否还有小写

6、把最后4列进行合并到一列

df_twitter_clean.info()

df_twitter_clean['stage'] = df_twitter_clean['doggo']+'|'+df_twitter_clean['floofer']+'|'+df_twitter_clean['pupper']+'|'+df_twitter_clean['puppo']
df_twitter_clean['stage'].head()

df_twitter_clean['stage']=df_twitter_clean.stage.str.lower().str.findall('(doggo|pupper|puppo|floof)')


# 查看有多个stage的数据
df_twitter_clean['stage_len'] = df_twitter_clean['stage'].map(lambda x: len(x))
df_twitter_clean['stage_len'].value_counts()

df_twitter_clean[df_twitter_clean['stage_len']>1]

可以看到,并非都是对一只狗狗的stage,为减少干扰,剔除有多种stage的12条数据

df_twitter_clean = df_twitter_clean[df_twitter_clean['stage_len'] <= 1]

df_twitter_clean['stage_len'].value_counts()

df_twitter_clean = df_twitter_clean.drop(columns=['stage_len']) 

df_twitter_clean['stage'].value_counts()

# #将缺失值替换为np.nan
df_twitter_clean['stage']=df_twitter_clean['stage'].apply(lambda x: '.'.join(x))
df_twitter_clean['stage']=df_twitter_clean['stage'].replace('',np.nan)
df_twitter_clean['stage'].value_counts()

df_twitter_clean['stage'].isnull().sum()

df_twitter_clean.head(2)


# 删除掉多余列
df_twitter_clean = df_twitter_clean.drop(columns=['doggo','floofer','pupper','puppo'])
df_twitter_clean.head(2)

(四) Combine data

# 1 查看3张表的数据情况
print(len(df_twitter_clean))
print(len(df_image_clean))
print(len(df_json_clean))

# recheck
df_twitter_clean.info()
df_image_clean.info()
df_json_clean.info()
df_twitter_clean.head(2)
df_image_clean.head(2)
df_json_clean.head(2)
# 2 合并数据-inner
# merge df_twitter_clean with df_json_clean 
main_df = df_twitter_clean.merge(df_json_clean, on='tweet_id',how='inner')

# Merge the archive_image and df_json_clean on tweet_id
master = main_df.merge(df_image_clean, on='tweet_id', how='inner')


master.head(2)
# master.info()

 (五) Storing

import os
os.getcwd() # 获取当前路径

# 保存处理后的数据
master.to_csv('twitter_archive_master.csv', index=False)

三、数据分析

(一)Q1: 哪个种类的狗狗最受欢迎?

types = master.p1.value_counts()
types
fig, ax = plt.subplots(figsize=(10,6))
plt.bar(types[0:10].index,types[0:10])  # 只对前10种进行展示
plt.xticks(rotation=45) # 设置x轴刻度角度,或者: plt.xticks(rotation='vertical') 
plt.xlabel('Dogs')
plt.ylabel('Number')
plt.title('Top 10 most popular dogs')
plt.show()

结论:可以看到,最受欢迎的狗狗是golden_retriever,其次是labrador_retriever和pembroke。

(二)Q2: 哪个名字给狗狗取的最多?

# from collections import Counter  # Counter是一个dict子类,主要是用来对你访问的对象的频率进行计数。
# x = master['name']
# count = Counter(x)
# count.most_common(11)

# 以上是通过导入Counter计数模块来查找出现频率最高的11项,也可以通过以下代码实现
master['name'].value_counts().nlargest(11) 
names = master['name'].value_counts().nlargest(10) 
fig, ax = plt.subplots(figsize=(10,6))
plt.bar(names.index,names)  # 只对前10种进行展示
plt.xticks(rotation=45) # 设置x轴刻度角度,或者: plt.xticks(rotation='vertical') 
plt.xlabel('Name')
plt.ylabel('Number')
plt.title('Top 10 most popular Names')
plt.show()

 

结论:可以看到,大多数推特中没有提到狗狗的名字,但在有名字的数据中,取名Cooper是最多的。

(三)Q3: 得分、喜爱数、转发数直接有什么关系?

master['score'] = master['rating_numerator']/master['rating_denominator']
master['score'].value_counts()
pd.plotting.scatter_matrix(master[['favorite_count','retweet_count','score']],figsize=(6,6),diagonal='hist')
plt.show()

 

plt.subplots(figsize=(10,6))
sns.heatmap(master[['favorite_count','retweet_count','score']].corr(),annot=True, vmax=1,vmin = 0, xticklabels= True, yticklabels= True, square=True, cmap="Reds")
plt.xticks(rotation=45)
plt.yticks(rotation=45)
plt.show()

 

结论:可以看到,rating_numerator 与 favorite_count、retweet_count 都没有什么明显的关系; 但是favorite_count与retweet_count有明显的线性关系,其相关系数也比较高,为0.93。

(四)Q4: 喜爱数、转发数随时间的变化关系?

time = pd.to_datetime(master[master.favorite_count < 80000].timestamp).dt.date

fig, ax = plt.subplots(figsize=(10,6))
plt.plot_date(time,master[master.favorite_count < 80000].retweet_count,color='y' , label='retweet_count')
plt.plot_date(time,master[master.favorite_count < 80000].favorite_count, color='r', label='favorite_count')
plt.xlabel('Date')
plt.ylabel('Number')
plt.title('The number of retweet and favorite change over time')
plt.legend()
ax.xaxis.set_tick_params(rotation=45, labelsize=10)  # 设置x轴ticks的角度,大小
plt.show()

 

master['rating'] = master['retweet_count']/master['favorite_count']

fig, ax = plt.subplots(figsize=(10,6))
# plt.plot_date(time,master[master.favorite_count < 80000].retweet_count,color='y' , label='retweet_count')
# plt.plot_date(time,master[master.favorite_count < 80000].favorite_count, color='r', label='favorite_count')
plt.plot_date(time,master[master.favorite_count < 80000].rating, label='rating')
plt.xlabel('Date')
plt.ylabel('rating')
plt.title('The Rating(retweet/favorite) change over time')
plt.legend()
ax.xaxis.set_tick_params(rotation=45, labelsize=10)  # 设置x轴ticks的角度,大小
plt.show()

 结论:可以看到, favorite_count一直比retweet_count要高,但是2016年9月以前,两者总体比较接近,之后两者总体差距变大。即 retweet_count与favorite_count的比率 Rating 有所下降。

(五)Q5:对不同stage的狗,推文中的描述有什么不同?(词云图)

from wordcloud import WordCloud
import re

master['text'][0]  # 查看文本情况

# all
mytext = ''
for i in master['text']:
    mytext = mytext + re.findall('(.*)https:',i)[0]
    mytext = mytext + "|"

# %pylab inline
# import matplotlib.pyplot as plt
wordcloud = WordCloud().generate(mytext)
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis("off")
plt.show()

 

(六)Q6: 不同stage狗狗的受欢迎情况

count_by_stage = master.stage.value_counts()
count_by_stage

mean_favorite_count = master.groupby('stage').favorite_count.mean()
mean_favorite_count

mean_retweet_count = master.groupby('stage').retweet_count.mean()
mean_retweet_count

fig, ax = plt.subplots(figsize=(10,6))
ax.bar(mean_favorite_count.index, mean_favorite_count, width=0.3, align='edge', alpha=0.8)
ax.bar(mean_retweet_count.index, mean_retweet_count, width=0.3, align='center', alpha=0.8)
ax.legend(labels=['mean_favorite_count','mean_retweet_count'],loc='best')
# ax.grid(b=False)

plt.xlabel('stage')
plt.ylabel('mean')
plt.title('The popularity of dogs at different stages')

ax2 = ax.twinx()
ax2.plot(count_by_stage.index, count_by_stage, 'y-')
ax2.legend(labels=['count_by_stage'],loc='best')
ax2.grid(b=False)
plt.ylabel('count')

 结论:对有stage状态的数据,可以看到评论数据条数中,
1、推文数:pupper、doggo、puppo、floof 的数据依次递减;
2、喜爱数:floof、pupper、doggo、puppo 的数据依次递减;
3、转发数:floof、pupper、doggo、puppo 的数据依次递减;

(七)Q7: source 情况分析

master.source.value_counts()

结论:绝大部分的推文都是通过 iPhone手机 发出的。

附:扩展-【聚合 pivot_table】


透视解析:
链接1:Python中pandas透视表pivot_table功能详解(非常简单易懂) - The-Chosen-One - 博客园
链接2:Python——数据透视(介绍pivot_table函数的使用方法) - 小猪课堂 - 博客园

# 透视表
# 会自动计算数据的平均值,但是我们也可以对该列元素进行计数或求和。要添加这些功能,使用aggfunc和np.sum就很容易实现
pd.pivot_table(master,index=['stage','source'],values=['score','favorite_count','retweet_count','rating'], aggfunc=np.mean)
# pd.pivot_table(master,index=['source','stage'],values=['score','favorite_count','retweet_count','rating'], aggfunc=len)

 

pd.pivot_table(master,index=['source','stage'],values=['score','favorite_count','retweet_count','rating'], aggfunc=[np.mean,len])

 

pd.pivot_table(master,index=['source','p1'],values=['tweet_id'], aggfunc=len)

 

# 对每个字段指定聚合方式
pd.pivot_table(master,index=['source'],values=['tweet_id','score'], aggfunc={'tweet_id': len, 'score': np.mean})

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值