数据清洗是对数据建立更严格模型和进行更深入分析的一个基础性工作,绝大部分的情况下,我们遇到的数据都存在一定的问题,我们需要对数据进行整理,以满足我们后续分析的需要。本文是作者在课程中的一个利用python进行数据清理的实战项目。项目中清洗的数据是关于推特主WeRateDogs中关于宠物狗评分的数据。
我们先导入需要用到的包资源:
# 导入需要的库
import pandas as pd
import numpy as np
import requests
import os
import json
import re
import datetime
导入数据:
#收集文件1和2
data1=pd.read_csv('image_predictions/image-predictions.tsv',sep='\t')
data2=pd.read_csv('twitter-archive-enhanced.csv')
# 收集文件 3 保存为 dataframe
data=[]
with open('tweet_json.txt','r') as f:
for line in f:
data.append(json.loads(line))
data3 = pd.DataFrame(data, columns=['id', 'retweet_count', 'favorite_count'])
data3.columns = ['tweet_id', 'retweet_count', 'favorite_count']
一共有包含三份数据,文件1是利用神经网络对图片进行识别结果的数据,文件2是推特包含的信息数据,文件3是关于每一条推特的转发和点赞的数据。
接下来的数据清洗一共分为三个部分:评估、问题总结和清理。首先评估数据,找到数据存在的问题,然后把存在的问题进行梳理,找到清洗该问题的方法;最后通过编程的方式进行清理。
评估
对数据进行评估是通过对数据一个观察,来初步了解数据中存在哪些问题,一般采用的是目测评估和编程评估。这里我们把问题类型分为整洁度问题和质量问题。
质量问题为数据的内容问题,常见的质量问题有:
- 数据丢失。
- 数据无效,如单元格中的值不符合常规,例如身高为负数。身高中具有"英寸" 和 “厘米" 等单位,因为身高的数据类型应为整数或浮点数。
- 数据不准确。
- 数据不一致,比如,使用不同的长度单位(英寸和厘米)。
整洁度问题是指数据比较杂乱,常见的整洁度问题有:
- 每个变量构成一列。
- 每个观察结果构成一行。
- 每种类型的观察单位构成一个表格。
1.目测评估
目测评估是查看整个数据集来查看数据中存在哪些问题。在jupyter中直接用输入data1,就可以查看数据:
#目测评估数据1
data1
通过目测未发现data1数据中有质量或者整洁度方面的问题
#目测评估数据2
pd.set_option('max_colwidth',200) #设置每一列显示的最大宽度,方便查看
data2
质量问题
- in_reply_to_status_id、in_reply_to_user_id、retweeted_status_id、retweeted_status_user_id、retweeted_status_timestamp这5列出现空值
- source这一列没有正确显示网页的格式
- text列中有些有RT @dog_rates:这样的字符,为转发的数据。
- name列中有非name字符
- source这个列名称没有正确表示其内容
- rating_numerator这一列的数据存在错误
- rating_denominator这一列数据存在错误
- 为了更直观,可以将timestamp这一列中+0000去掉
整洁度问题
- data2描述狗狗地位有四列,一个特征的观察应该放在一列里面
#目测评估数据3
data3
目测没有发现data3的数据存在质量或者整洁度问题
2.编程评估
# 使用 pandas 的各种方法评估三个数据集,比如 info value_counts 等
data1.info()
len(data1['tweet_id'].unique())
从上面的信息可以看出data1数据没有空值而且tweet_id这一列没有重复值
data2.info()
len(data2['tweet_id'].unique())
in_reply_to_status_id、in_reply_to_user_id、retweeted_status_id、retweeted_status_user_id、retweeted_status_timestamp这5列出现空值
data3.info()
len(data3['tweet_id'].unique())
favorite_count和retweet_id这两列的格式都为字符串
问题总结
质量问题
data2
表格
- in_reply_to_status_id、in_reply_to_user_id、retweeted_status_id、retweeted_status_user_id、retweeted_status_timestamp这5列出现空值
- source这一列没有正确显示网页的格式
- text列中有些有RT @dog_rates:这样的字符,为转发的数据。
- name列中出现非name字符
- source这个列名称没有正确表示其内容
- rating_numerator这一列的数据存在错误
- rating_denominator这一列数据存在错误
- 为了更直观,可以将timestamp这一列中+0000去掉
- 项目要求只要有图片数据
- timestamp列的格式
- tweet_id列为整型
data3
表格
- tweet_id这列的格式都为整型
data1
表格
- tweet_id这列格式为整型
整洁度问题
- data2中描述狗狗地位的有四列。
- 分析中有三份数据,对一个对象的观察应该形成一份数据
清理
对所有问题的清理,我们按照问题描述——定义——代码——测试的框架进行处理。定义是对解决问题的方法进行描述,代码是解决问题所使用的代码,测试是检验问题有没有得到解决。
# 备份三个数据集
df1=data1.copy()
df2=data2.copy()
df3=data3.copy()
问题描述一
- df2表格数据中in_reply_to_status_id、in_reply_to_user_id、retweeted_status_id、retweeted_status_user_id、retweeted_status_timestamp这5列出现空值
定义
- 由于对于我所要进行的分析,这五列都用不上。所以我对这五列的处理方法是删除。
代码
# 解决问题一的代码
df2.drop(['in_reply_to_status_id','in_reply_to_user_id','retweeted_status_id','retweeted_status_user_id','retweeted_status_timestamp'],axis=1,inplace=True)
测试
# 测试问题一是否正确清理完成
df2.info()
问题描述二
- df2表格数据中source这一列没有正确显示网页的格式
定义
- 去除多余的字符
代码
#解决问题二的代码
def func_df_apply2(df):
href=re.search('.*?"(.*?)"',df['source'],re.I|re.S)
source=href.group(1)
return source
df2['source']=df2.apply(func_df_apply2,axis=1)
测试
# 测试问题二是否正确清理完成
df2['source'].value_counts()
问题描述三
- df2中text列中有些有RT @dog_rates:这样的字符,为转发的数据。
定义
- 找出这些列并删除
代码
#解决问题三的代码
drop_row=[]
for n in range(len(df2['text'])):
text=re.match('RT @.*?:(.*)',df2['text'][n],re.S|re.I)
if text!=None:
drop_row.append(n)
df2.drop(drop_row,axis=0,inplace=True)
测试
#测试问题三有没有完成
len(df2['text'])
问题四描述
- name列中出现非name字符
定义
- 将name列中非name字符改成np.nan
代码
#解决问题四的代码
mask=(df2.name.str.islower())|(df2.name=='None')|(df2.name=='a')|(df2.name=='an')
df2.loc[mask,'name']=np.nan
测试
#测试问题四有没有完成
mask=(df2.name.str.islower())|(df2.name=='None')|(df2.name=='a')|(df2.name=='an')
mask.value_counts()
问题描述五
- source这个列名称没有正确表示其内容
定义
- 将source这个列名改为tweet_download_url
代码
#解决问题五的代码
df2=df2.rename(index=str,columns={'source':'tweet_download_url'})
测试
#测试问题五有没有完成
df2.head(1)
问题描述六
- rating_numerator这一列的数据存在错误;rating_denominator这一列数据存在错误。
定义
- 从text列中提取评分修改rating_nunmerator这一列;从text列中提取数据修改rating_denominator这一列
代码
#解决问题六的代码
df2.drop(['516','342','1663'],axis=0,inplace=True)
def func_df_apply(df):
rate=re.findall('\d+\.\d+/\d+|\d+/\d+',df.text)
if len(rate)!=1:
n=rate[1].split('/')
df['rating_numerator']=int(n[0])
df['rating_denominator']=int(n[1])
else:
rate_str=rate[0].split('/')
m=int(rate_str[1])/10
if m==1:
df['rating_numerator']=float(rate_str[0])
df['rating_denominator']=int(rate_str[1])
else:
df['rating_numerator']=int(rate_str[0])/m
df['rating_denominator']=int(rate_str[1])/m
return df
df2=df2.apply(func_df_apply,axis=1)
测试
#测试问题六有没有完成的代码
pd.set_option('max_colwidth',200)
df2.query('rating_denominator!=10 | rating_numerator>20')[['text','rating_numerator','rating_denominator']]
问题描述七
- 为了更直观,可以将timestamp这一列中+0000去掉
定义
- 将timestamp这一列中+0000去掉
代码
#解决问题七的代码
timestamp=[]
for n in df2['timestamp']:
time=re.search('(.*?) \\+0000',n)
timestamp.append(time.group(1))
df2['timestamp']=timestamp
测试
#测试问题八有没有完成
df2.head(20)
问题描述八
- df1、df2、df3表格数据中tweet_id这列的格式都为整型
定义
- 将tweet_id这列的格式转换为字符串
代码
#解决问题八的代码
df1[['tweet_id']]=df1[['tweet_id']].astype(str)
df2[['tweet_id']]=df2[['tweet_id']].astype(str)
df3[['tweet_id']]=df3[['tweet_id']].astype(str)
测试
#测试是问题八有没有完成
df3.info()
问题描述九
- df2的timestamp列的格式为字符串
定义
- 将timestamp这列的格式转换为datetime类型
代码
#解决代码九的代码
def func_df_apply(df):
timestamp=datetime.datetime.strptime(df['timestamp'],'%Y-%m-%d %H:%M:%S')
return timestamp
df2['timestamp']=df2.apply(func_df_apply,axis=1)
测试
#测试问题九有没有完成
df2.info()
问题描述十
- data2数据中描述狗狗地位的由四列
定义
- 将doggo、floofer 、pupper 、puppo这四列应该合并成一列
代码
#解决问题十的代码
df2['stage']=df2['timestamp']
df2.drop(['doggo','pupper','puppo','floofer'],axis=1,inplace=True)
def func_df_apply(df):
a=re.findall(r'doggo|floofer|pupper|puppo',df.text)
if a==[]:
df['stage']=np.nan
else:
a=set(a)
df['stage']=','.join(a)
return df
df2=df2.apply(func_df_apply,axis=1)
测试
#测试问题十有没有完成
df2['stage'].value_counts()
问题描述十一
- 对一个对象的观察应该形成一份数据,项目只要求由图片的数据
定义
- 将三份数据合并成一份数据,并去除掉无图片数据
代码
#解决问题十一的代码
df_clean=pd.merge(df1,df3,on='tweet_id',how='inner')
df_clean=pd.merge(df_clean,df2,on='tweet_id',how='inner')
测试
#测试问题十一有没有完成
df_clean.info()
存储清理后的主数据集
# 将清理后的主数据集保存为 csv 文件或者 SQLite 数据库文件
df_clean.to_csv("twitter_archive_master.csv",index=False,sep=',')