这篇的文章主要目的是,根据提供的朝阳区医院2016年销售数据.xlsx数据表,得出月均消费次数、月均消费金额、客单价和消费趋势这五个结论。
阅读路线:
- 数据导入
- 数据清洗
- 数据分析
数据导入
为了方便,我把excel文件另存为了csv文件,并且重命名为2016.csv,然后放在Desktop文件夹下。
设置工作路径
>setwd("C:\\Users\\lkx\\Desktop")
查看下到底是不是这个工作路径
>getwd()
[1] "C:/Users/lkx/Desktop" #确实是的
利用read.table()读入csv文件
> import.csv<-read.table("2016.csv",header = TRUE,sep=",")
# 其中"2016.csv"就是我们要读入的文件;header=TRUE表示读入变量(就是我们excel中见到的表头);sep=","表示以逗号为分隔符
我们读取前六行先简单看一下数据
> head(import.csv)
购药时间 社保卡号 商品编码 商品名称 销售数量 应收金额 实收金额
1 2016-01-01 星期五 1616528 236701 三九感冒灵 7 196 182.00
2 2016-01-02 星期六 1616528 236701 三九感冒灵 3 84 84.00
3 2016-01-06 星期三 10070343428 236701 三九感冒灵 3 84 73.92
4 2016-01-11 星期一 13389528 236701 三九感冒灵 1 28 28.00
5 2016-01-15 星期五 101554328 236701 三九感冒灵 8 224 208.00
6 2016-01-20 星期三 13389528 236701 三九感冒灵 1 28 28.00
其实我们读入的数据共有6577行,7列
如果点击上图中箭头所指向的位置就能够看到所有的数据:
数据处理
当我们拿到数据的时候不应该着急去计算,先观察数据,看数据是否规整、是否符合我们的需要。根据我们的分析要求将做五个方面的修整:列名重命名、删除缺失数据、处理日期、数据类型转换和数据排序
- 列名重命名
把列名汉语名称变为英文状态下的名称将会编程环境更加适合。这里我们通过names()函数来重命名变量。
>names(import.csv)<- c("time","cardno","drugld","drugName","saleNumber","virtualmoney","actualmoney")
- 删除缺失数据
在任何规模的项目中,数据都可能由于未作答问题、设备故障或误编码数据的缘故而不完整。缺失值以符号NA(Not Available,不可用)表示。函数is.na()检测缺失值是否存在的
举例子:
> y<-c(1,2,3,NA)
> is.na(y)
[1] FALSE FALSE FALSE TRUE
注意:
- is.na()函数是如何作用于一个对象上的,它将返回一个相同大小的对象,如果某个元素是缺失值,相应的位置将被改写为TRUE,不是缺失值的位置则为FALSE。
- 但是我们也应该注意到那些因为未作答问题、设备故障或错误编码数据得来的数据,不一定都是用NA来表示的,比如说因为错误得到的正无穷或负无穷都是Inf或-Inf来表示的,就需要用is.infinite()来检测了。
- 再者说,对于数据中某些特别怪异的值,比说在这个数据框变量销售数量中有一个值为100000000,我们看到这样的值与其他的差别如此之大,我们就可以重编码这个值为缺失值
所以接下来我们做的处理是:
> import.csv<-import.csv[!is.na(import.csv$time),]
#import.cav$time 表示选取time这一列
#is.na(import.csv$time)返回一个和import.csvs$time相同大小的对象,如果元素是缺失值,相应的位置将被改写为TRUE,不是缺失值的位置则为FASLE
#'!'是非的意思,!is.na(import.csv$time)将和is.na(import.csv$time)是完全相反的结果
#import.csv[!is.na(import.csv$time),]表示只要time这一列中判断条件为TRUE的对象;也即是意味着,删除time列中为缺失值的所有行。
其实,我们还可以通过函数na.omit()移除所有含有缺失值的观测。 na.omit()可以删除所有含有缺失数据的行。
- 处理日期
我们可以看到在购药时间这一变量下的值中含有"星期五","星期六",这样的字符串,其实这些字符串对我们的分析是没有作用,所以决定把购药时间这一变量下的值进行分裂,并删除含有"星期五","星期六"这诸如此类的字符串。我们决定是使用stringr包中的str_split_fixed()函数进行分割。
> install.packages('stringr') #stringr安装
> library(stringr)
str_split_fixed()函数定义:str_split_fixed(string, pattern, n)
参数列表:
- string: 字符串,字符串向量;
- pattern: 匹配的字符。
- n: 分割个数
> timesplit<-str_split_fixed(import.csv$time, " ",2)
> class(timesplit) # 用str_split_fixed()函数分割,结果类型是matrix
[1] "matrix"
> import.csv$time<-timesplit[,1]
> head(import.csv) #检查是否分裂成功
time cardno drugld drugName saleNumber virtualmoney actualmoney
1 2016-01-01 1616528 236701 三九感冒灵 7 196 182.00
2 2016-01-02 1616528 236701 三九感冒灵 3 84 84.00
3 2016-01-06 10070343428 236701 三九感冒灵 3 84 73.92
4 2016-01-11 13389528 236701 三九感冒灵 1 28 28.00
5 2016-01-15 101554328 236701 三九感冒灵 8 224 208.00
6 2016-01-20 13389528 236701 三九感冒灵 1 28 28.00
由于import.csv$time是为字符型变量,应该转化为日期型变量。可用函数as.Date()执行这种转化。其语法为as.Date(x, "input_format"),其中x是字符型数据, input_format则给出了用于读入日期的适当格式
> class(import.csv$time)
[1] "character" #字符串型
> import.csv$time<-as.Date(import.csv$time,"%Y-%m-%d")
> head(import.csv)
time cardno drugld drugName saleNumber virtualmoney actualmoney
1 2016-01-01 1616528 236701 三九感冒灵 7 196 182.00
2 2016-01-02 1616528 236701 三九感冒灵 3 84 84.00
3 2016-01-06 10070343428 236701 三九感冒灵 3 84 73.92
4 2016-01-11 13389528 236701 三九感冒灵 1 28 28.00
5 2016-01-15 101554328 236701 三九感冒灵 8 224 208.00
6 2016-01-20 13389528 236701 三九感冒灵 1 28 28.00
> class(import.csv$time)
[1] "Date"
- 数据类型转换
为了使销售数量、应收金额、实收金额下的数据类型都为数值型,所以都做了以下的转换。
> import.csv$saleNumber<-as.numeric(import.csv$saleNumber)
> import.csv$virtualmoney<-as.numeric(import.csv$virtualmoney)
> import.csv$actualmoney<-as.numeric(import.csv$actualmoney)
- 数据排序
在R中,可以使用order()函数对一个数据框进行排序。默认的排序顺序是升序。添加decreasing=TRUE时为降序即可得到降序的排序结果。
> import.csv<-import.csv[order(import.csv$time,decreasing = TRUE),]
> head(import.csv)
time cardno drugld drugName saleNumber virtualmoney actualmoney
57 2016-07-19 1616528 236701 清热解毒口服液 1 28 28
862 2016-07-19 10013306428 2367011 开博通 1 31 28
863 2016-07-19 10030713328 2367011 开博通 4 124 118
864 2016-07-19 10059383628 2367011 开博通 2 62 56
865 2016-07-19 101409528 2367011 开博通 2 62 56
866 2016-07-19 13406628 2367011 开博通 2 62 56
数据分析
- 月消费次数
月均消费次数=总消费次数 / 月份数,并且同一天内,同一个人发生的所有消费算作一次消费。对于同一天和同一个人,我们可以认为是时间和社保卡号(暂且认为一个人使用一张社保卡)都是相同的,然后把同一人和同一天的重复项删除,即可得到总消费次数。
> kpi1<-import.csv[!duplicated(import.csv[,c("time","cardno")]),]
> consumernumber<-nrow(kpi1)
> consumernumber
[1] 5398
其中,duplicated 判断对象的每个取值是否重复,如duplicated(c(1,1,2,3)) 返回 FALSE TRUE FALSE FALSE ,其中TRUE对应的为重复的值。
判断月份数:刚刚我们关于时间的排序选择的是降序,所以时间这一序列下第一个值是最大值,最后一个值是最小值。我们用最大值减去最小值就能够得出实际天数,因为每月是30天,所以相除就能得到月数。
> starttime<-kpi1$time[1]
> endtime<-kpi1$time[nrow(kpi1)]
> day<-starttime-endtime
> month<-day%/%30
Error in Ops.difftime(day, 30) : '%/%'对"difftime"对象不适用
> class(day) #查看day的类别
[1] "difftime"
> day<-as.numeric(day) #转换成数值型
> day
[1] 200
> month<-day%/%30 #%/% 表示取整
> month
[1] 6
所以:月均消费次数=总消费次数 / 月份数
> monthconsume<-consumernumber%/%month
> monthconsume
[1] 899
- 月均消费金额
月均消费金额=总消费金额/ 月份数,这里的总消费金额,我们选择的是实收金额的取和得来的。
> totalmoney<-sum(import.csv$actualmoney,na.rm=TRUE)
> monthmoney<-totalmoney/month
> monthmoney
[1] 50771.71
原因如果实收金额中有一个缺失值的话,默认选项na.rm=FALSE会导致R函数sum返回NA,添加na.rm=TRUE就不会出错了。
- 客单价
客单价=总消费金额 / 总消费次数
>pct<-totalmoney/consumernumber
> pct
[1] 56.43391
- 消费趋势
首先让实际消费金额按照日期(周)的分类进行求和得到下面的数据。
> week<-tapply(import.csv$actualmoney,format(import.csv$time,"%Y-%U"),sum)
#format(import.csv$time,"%Y-%U")表示将import.csv$time转化成这种"%Y-%U"格式
#"Y%"表示四位数的年份,"%U"表示第多少周(一年共有52周,从第0周开始);
#tapply(import.csv$actualmoney,format(import.csv$time,"%Y-%U"),sum)
表示将实际消费金额按照"%Y-%U"这种日期格式进行分类并求和
> week #得到了这样的一组数据
2016-00 2016-01 2016-02 2016-03 2016-04 2016-05 2016-06 2016-07 2016-08 2016-09 2016-10
1972.80 9679.64 10979.01 8719.73 15662.30 18758.82 3665.70 8441.51 8453.57 9988.98 8500.78
2016-11 2016-12 2016-13 2016-14 2016-15 2016-16 2016-17 2016-18 2016-19 2016-20 2016-21
9869.16 10135.23 8426.46 11400.66 14408.21 10385.33 10265.98 9496.06 9728.40 11794.11 11497.20
2016-22 2016-23 2016-24 2016-25 2016-26 2016-27 2016-28 2016-29
9530.38 10806.71 11877.43 14077.38 10894.90 8386.97 13372.67 3454.18
将把数据转换成数据框并再添加一行
> week<-as.data.frame.table(week) #as.data.frame.table(),这个函数我也没有弄清,自己就选择先记住了
> week #这是部分的数据
Var1 Freq
1 2016-00 1972.80
2 2016-01 9679.64
3 2016-02 10979.01
4 2016-03 8719.73
5 2016-04 15662.30
> names(week)<-c("time","actualmoney")
#names():这个函数可以返回一个向量,显示的是用做参数的类型里所有可以用"$"访问的变量名
> week$time<-as.character(week$time)
> week$timeNumber<-(1:nrow(week)) #添加一行
开始画消费趋势图
> plot(week$timeNumber,week$actualmoney,xlab = "时间(年份-第几周)",ylab="消费金额",xaxt="n",main="2016年朝阳区医院消费曲线",col="blue",type="b")
#xaxt="n"就是先不显示x的刻度,通过axis函数自定义
> axis(1,at=week$timeNumber,labels = week$time,cex.axis=1.5)
#1是表示你要对x轴作修改,labels就是要打的内容,
#at就是你要多少个刻度,cex.axis=1.5表示坐标轴文字放大1.5倍
总结下:月均消费次数为899,月均消费金额为50771.71,客单价为56.43391,消费趋势如下图
通过这几天的学习明显的感觉到了制定计划的极大好处,制定计划之后自己再也不会为什么都没有学会、没有学好而感到焦灼了,而按照计划表慢慢来一步一步实现小目标,不断带来小惊喜。这里应该特别感谢猴哥,因为自己原来一直有计划无用之类的错误概念,是猴哥通过作业的方式倒逼自己做了计划。其次我们的通关都是以文章的形式来呈现,在写的过程中不知不觉的梳理清楚好多概念性问题,也是应了教是最好的学习方式这句话。感谢猴哥。
网友评论