近期做了四个Python数据分析的案例实践,把流程、语句和想法归纳总结,做一个小小的里程碑
1.流程
明确分析目的->由数据源构建指标体系->数据清洗->数据处理及分析->结论
2.明确分析目的
-->分析问题产生的原因/分析业务现状/通过分析对业务进行指导……
目的——是具体的、充满定语的、具有返回值的
3.由数据源构建指标体系
区分维度和指标:
维度:对应数据的每一列,多个列组合也可以认为是一个维度
指标:各种统计值
在不同维度上得到各种指标
根据不同数据源和分析目的搭建指标体系
eg:一款付费软件的日活/人均付费/付费渠道/付费功能/使用频次/使用时长……
4.数据清洗
4.1导入库
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
4.2加载文件
df = pd.read_csv('./file.csv')
4.3简单查看数据有哪些列&类型
df.info() # 可看到缺失行在某一列上的条目数少于总数,但当列数目较多时,不容易发现
df.head()
4.4查看数据的整体信息
df.describe() # 整数型或浮点型数据
df.count() # 查看各列非空数据量
4.5开始数据清理
->对需要分析的维度进行清理
->对于数值型数据,可以通过describe方法输出信息,关注最大值、最小值、平均值、行数等是否符合常理或是否满足本次数据分析目的对数据的要求
4.5.1判断数据是否有null值、0值等
若null值所在行数较少,删除后不会对总体数据产生较大影响
df[df.salary.isnull()] 输出salary列null值所在行的全部信息
df.drop(df[df.salary.isnull()].index, inplace = True)
df[df.salary.isnull()] # 查看是否删除成功
若不想删除数据,则可用聚合函数或前值/后值等替换处理,如使用平均数替换
df['salary'].replace(0,df['salary'].mean(), inplace = True)
# 注:若遇到较多0值,且要采用平均数填充,可先将0值替换为np.nan,再使用列名.fillna()填充该列数据平均数,这样在计算平均数时nan所在行不会对整体平均数产生影响(不计入在内)
df.salary.replace(0,np.nan, inplace = True)
df.salary.fillna(df['salary'].mean(), inplace=Ture)
df['communityAverage'].fillna(df['communityAverage'].mean(), inplace=True) # 对null值,也可使用fillna
df.info() 查看是否有Nan值
Nan非null值,无法用 .isnull()判断
但通过df.info()判断存在缺失值,但又不是null时,可查看其是否为Nan值
df['Type'].value_counts(dropna = False) #统计包括Nan在内的数据
data.dropna() # 默认axis=0,how='any',删除带有空值的行,只要有一个空值,就删除整行
df.dropna(how='all') # 整行都是空值时,才会被删除
df.dropna(how='all',axis=1)) # 整列都是空值时,才会被删除
df.dropna(subset=[1,2]) # 删除指定1和2列中包含缺失值的行
4.5.2 查看是否具有重复值,展示重复的条目
如果有重复值,⼀般最后处理,因为其他的列可能会影响到删除那⼀条重复的记录
先处理其他的列
df[df.duplicated()] #注意这样使用是展示各字段完全相同的条目
df.drop_duplicates('App', inplace = True)# 删除'App'列中数据重复的行
也可根据指定部分列(待分析列)是否具有重复值
df[df[:,['employee_ID','deptno_ID','salary']].dupliacted()]
df[df.employee_ID.dupliacted()] # 单独查看employee_ID是否具有重复值的条目
或使用行数是否相等来判断
len(df.empolyee_ID.unique()) 与 len(df) # 是否相等,相等->该列无重复
df['employee_ID'].value_counts() # 默认剔除nan值 # 是否有大于1的
df['employee_ID'].value_counts(dropna = False) # 计算包含nan值的条目
pd.unique(df['employee_ID']).size # 是否与整个数据行数相同
注意:当我们对⼀列取size属性的时候,返回的是⾏数,如果对于dataframe使⽤size,返回的是行乘以列的结果,也就是总的元素数
4.5.3 删除非所需数据
df.drop(df[df['hiretime'].dt.year<2015].index, inplace = True)
df.drop(df[df['hiretime'].dt.year>2019].index, inplace = True)
df['hiretime'].dt.year.value_counts()
4.5.4 使用分类查看是否有不合逻辑的类型
df['nationality'].value_counts()
比如该列都是用国家英文全称来填充的,但某个结果中若出现数字-->不合逻辑-->是因为没有把数字标号转换为国家还是因为其他原因?
4.5.5 判断字符串类型数据是否只由数字组成
df['deptno_info'].str.isnumeric() --> True or False
df['deptno_info'].str.isnumeric().sum() -->True 的条目数
数据格式转换
df['deptno_info'].astype('i8') #将本列转换为数值格式
说明:
isnumeric() 方法检测字符串是否只由数字组成。这种方法是只针对unicode对象。
注:定义一个字符串为Unicode,只需要在字符串前添加 'u' 前缀即可,如 str1 = u'Itachi';
对于 Unicode 数字、全角数字(双字节)、罗马数字和汉字数字会返回 True ,其他会返回 False。byte数字(单字节)无此方法。
print u'123'.isnumeric() # True
print u'Ⅷ'.isnumeric() # True
print u'abc123'.isnumeric() # False
print u'1.23'.isnumeric() # False
4.5.6 单位转化 str.replace()
eg:数据单位的简化处理
tips:转化为数据较小单位时可以实现-->转化后数据成为整数int类型,而非float
df['size'] = df['size'].replace('M','e+6') #M-->10^6
df['size'] = df['size'].replace('k','e+3') #k-->10^3
df['size'] = df['size'].replace(',','') # 替换千位符
df['Size'].astype('f8')
使用is_convertable(v)函数定义一个方法,判断字符串判断是否可以转换
def is_convertable(v):
try:
float(v)
return True
except ValueError:
return False
查看不能转换的字符串分布
temp = df['size'].apply(is_convertable) -->能转换 True
df['size'][-temp].value_counts() --> -temp 取False的
# 转换剩下的字符串
df['size'] = df['size'].str.replace('Varies with device','0')
# 再看下是不是还有没转换的字符串
temp = df['size'].apply(is_convertable)
df['size'][-temp].value_counts()
# 转换类型
# e+5这种格式使⽤astype直接转为int有问题,如果想转成int,可以先转成f8,再转i8
#df['size'] = df['size'].astype('f8').astype('i8')
df['size'] = df['size'].astype('i8')
# 将size为0的填充为平均数
df['size'].replace(0,df['size'].mean(), inplace = True)
df.describe()
5.数据分析
5.1样本总数
df.count()
5.2数值类型列的常见统计指标
df.describe()
5.3采用不同维度和指标进行分析
① 分组 groupby
分组统计不同国家人员并保留两列
nationality_data = df.groupby('nationality',as_index = False).count()[['nationality', 'ID']]
nationality_data.rename(columns={'ID':'employee_count'},inplace = True) #重命名ID列-->上述操作后ID列实际为总数
不同部门中雇员平均工资
deptno_data = df.groupby('deptno',as_index = False).mean()[['deptno','salary']
对不同分组下另一维度进行排序
eg:对App分类分组后计算其他维度平均值,并按照平均安装量对各组降序排列
df.groupby('category').mean().sort_values('installs', ascending = False)
多列分组
# type --> free or charges
# category --> family or media or chat ..........
df.groupby(['type','category'].mean().sort_values('reviews', ascending = True/False, inplace = True/False)
② 分类计数
groupby().count()
df.groupby('nationality').count()
value_counts()
eg:统计每年入职人数
年份跨度较大时,有些年份可能数据较少;年初或年中时,当年数据可能较少
看下每年数据的数量,确定是否要删除数据少的年份
将字符串转化为日期格式,方便使用内置函数
df['hiretime'] = pd.to_datetime(df['hiretime'])
df['hiretime'].dt.year.value_counts()
分类计算比例
eg:对类型和分类分组后,对其评论安装比进行排序
g = df.groupby(['type','category']).mean()
(g['reviews'] / g['installs']).sort_values(ascending = False)
③ 按照数值排序 sort_values()
nationality_sorted_data = nationality_data.sort_values('employee_count', ascending=False)
nationality_sorted_data[nationality_sorted_data.employee_count>100] # 显示employee_count数量大于100的数据列表
df.sort_index(inplace = True) # 按照索引排序 sort_index()
④ 分桶 pd.cut-groupby
雇员年龄分布
生成桶,5岁一个分桶 最小18岁,最大60岁
bins = np.arange(18,63,5)
bins_data = pd.cut(df['age'],bins)
bin_counts = df['age'].groupby(bins_data).count()
# 为图表展示好看使用列表生成式处理index
bin_counts.index = [str(x.left) + '-' + str(x.right) for x in bin_counts.index]
⑤ 单独取出某列
eg:取出2017年,国籍为China的所有数据
df_2017 = df[df['hiretime'].dt.year == 2017]
df_2017_China = df_2017[df_2017['nationality'] == 'China']
df_2017_China.describe()
⑥ 占比 len()/len()
统计salary在50~100之间的数据在所有数据中的占比
len(df[df['salary'] > 50]) & len(df[df['salary'] < 100])/len(df)
⑦ 相关性(0.5以上可以认为相关,0.3以上可以认为是弱相关)
df.corr() # 列出二维相关性表
⑧ .head() .tail()
# 取某维度的前十名和后十名
print(product_orderby_count.head(10))
print(product_orderby_count.tail(10))
⑨ .intersection() 交集
看下销售额和销量最后100个的交集,如果销售额和销量都不行,这些商品需要看看是不是要优化或者下架
problem_productids = productid_turnover.tail(100).intersection(product_orderby_count.tail(100).index)
5.4 matplotlib查看数据效果
bin_counts.plot()
plt.show()
注意点:
1.drop、rename等方法具有inplace参数,具体见帮助文档,学习初期往往忽略此参数,导致数据未在原表上修改或未设置接收变量。运行后无输出则说明inplace为TRUE(jupyter notebook的使用优点),若有输出则应赋予接收变量或为inplace赋值;而replace没有inplace参数,需要设置接收值
2.对多列进行操作时,留心是否需要多加一层括号,如 df.groupby(['district', 'elevator'])
3.注意到df_dis_want[(df['year'] == '2017')]判别条件这里,数据类型不同判别条件中需要考虑是否加引号'',或者可认为是数据清洗环节的疏漏