R语言S4类应用的一个简单例子
2017-09-29 00:06
459 查看
原文见此处。
做了翻译的尝试:
最近我与来自WLOG Solutions的朋友讨论了银行现金流管理引擎在R中的实现问题。实现代码漂亮地展示了S4类的使用,所以我想这应该是S4类的一个很好的例子。
当要设计一个求解方案来为多个现金获取点提供预测值时,萌发了使用面向对象方法的一个典型应用例子。对于每一个现金获取点,我们有可供利用的具有相同结构的历史数据,并能基于此获得余额预测值。然而,在不同的现金获取点,顾客对现金有不同的使用特征,这将需要应用不同的统计预测模型。例如,在ATM现金获取点,顾客可以只取现,但在分行点,顾客也可以存款。因此,我们使用S4类来建模。
下文给出了实现的代码。首先使用setGeneric() 函数来创建一个泛函接口givePrediction(),泛函givePrediction()将会基于不同的对象类型来调度合适的方法(多态)。接着我们使用setClass() 函数创建了S4类型的Bank类、ATM类、Branch类,并为这些类各自创建givePrediction方法。在我们的例子中,在ATM类中使用线性回归模型,在Branch类中简单地使用均值来进行余额预测。注意到我们也为CashPoint类定义了givePrediction方法,如果没有被其子类的合适方法(givePrediction方法)覆盖的话,CashPoint类的givePrediction方法将会被调用(stop(“no givePrediction method for this class”),无法为CashPoint 类创建实例,因为其是一个虚类)。
通过new一个Bank类实例并调用givePrediction() 函数,代码得以运行。Bank 类构造器从bank_model.txt文档读取银行关于现金获取点的结构数据,bank_model.txt文档包括两列,一列是现金获取点的ID,另一列是现金获取点的类型(ATM或Branch分行点)。接着调用setClass() 函数创建CashPoint类。每一个CashPoint类实例都将通过读取branch_balances_data.txt文档的方式被初始化,branch_balances_data.txt文档有三个列:BranchId(现金获取点的ID),Date(日期), Balance(余额)。初始化CashPoint类实例将通过BranchId列来subset合适的子集。实例化的CashPoint对象的balances字段存放了与该CashPoint实例对象对应的历史数据(即Date(日期), Balance(余额))。Bank 类被实例化之后,givePrediction()将被调度并作用在Bank 类实例。在此过程,根据程序运行时现金获取点的类型(ATM或Branch分行点), 将通过S4类系统自动调用ATM or Branch类的 givePrediction()方法。
CashPoint_1;ATM
CashPoint_2;Branch
CashPoint_3;ATM
CashPoint_4;Branch
CashPoint_5;ATM
CashPoint_1;2012-12-01;423000
CashPoint_1;2012-12-02;312000
CashPoint_1;2012-12-03;220000
CashPoint_1;2012-12-04;123000
CashPoint_2;2012-12-01;223000
CashPoint_2;2012-12-02;212000
CashPoint_2;2012-12-03;320000
CashPoint_2;2012-12-04;223000
CashPoint_3;2012-12-01;323000
CashPoint_3;2012-12-02;312000
CashPoint_3;2012-12-03;270000
CashPoint_3;2012-12-04;223000
CashPoint_4;2012-12-01;323000
CashPoint_4;2012-12-02;412000
CashPoint_4;2012-12-03;320000
CashPoint_4;2012-12-04;373000
CashPoint_5;2012-12-01;223000
CashPoint_5;2012-12-02;192000
CashPoint_5;2012-12-03;150000
CashPoint_5;2012-12-04;133000
做了翻译的尝试:
最近我与来自WLOG Solutions的朋友讨论了银行现金流管理引擎在R中的实现问题。实现代码漂亮地展示了S4类的使用,所以我想这应该是S4类的一个很好的例子。
定义问题
每一家商业银行都需要为其顾客提供现金获取入口,例如旗下数以百计的现金获取点(ATM自助取款机)或者分行点。银行管理者面临的这个问题需要解决三个冲突的目标:(1)他们必须确保在现金获取点有足够的现金以保证流动性;(2)他们需要最小化冻结资金的数量,因为它们对银行的经营不起作用;(3)他们需要最小化从金库到现金获取点的运输成本。这是一个复杂的优化问题,尤其是,它涉及到了,需要通过使用历史数据预测每天在现金获取点的现金余额。当要设计一个求解方案来为多个现金获取点提供预测值时,萌发了使用面向对象方法的一个典型应用例子。对于每一个现金获取点,我们有可供利用的具有相同结构的历史数据,并能基于此获得余额预测值。然而,在不同的现金获取点,顾客对现金有不同的使用特征,这将需要应用不同的统计预测模型。例如,在ATM现金获取点,顾客可以只取现,但在分行点,顾客也可以存款。因此,我们使用S4类来建模。
实现方案
定义一个具有CashPoints 属性(现金获取点属性,其属性值是许多的不同的现金获取点)的Bank类。再定义一个CashPoint 类,为虚类(不能被实例化),再接着定义的ATM 类和Branch类将继承自CashPoint类。 下图展示了类之间的结构。每一个CashPoint类的实例将有“历史余额(balances)”属性(该实例还有关于现金获取点的id属性,为字符类型,“历史余额”属性为数据框类型),还有一个能提供预测值的givePrediction方法。该givePrediction方法基于不同的ATM 类和Branch类有不同的实现。下文给出了实现的代码。首先使用setGeneric() 函数来创建一个泛函接口givePrediction(),泛函givePrediction()将会基于不同的对象类型来调度合适的方法(多态)。接着我们使用setClass() 函数创建了S4类型的Bank类、ATM类、Branch类,并为这些类各自创建givePrediction方法。在我们的例子中,在ATM类中使用线性回归模型,在Branch类中简单地使用均值来进行余额预测。注意到我们也为CashPoint类定义了givePrediction方法,如果没有被其子类的合适方法(givePrediction方法)覆盖的话,CashPoint类的givePrediction方法将会被调用(stop(“no givePrediction method for this class”),无法为CashPoint 类创建实例,因为其是一个虚类)。
通过new一个Bank类实例并调用givePrediction() 函数,代码得以运行。Bank 类构造器从bank_model.txt文档读取银行关于现金获取点的结构数据,bank_model.txt文档包括两列,一列是现金获取点的ID,另一列是现金获取点的类型(ATM或Branch分行点)。接着调用setClass() 函数创建CashPoint类。每一个CashPoint类实例都将通过读取branch_balances_data.txt文档的方式被初始化,branch_balances_data.txt文档有三个列:BranchId(现金获取点的ID),Date(日期), Balance(余额)。初始化CashPoint类实例将通过BranchId列来subset合适的子集。实例化的CashPoint对象的balances字段存放了与该CashPoint实例对象对应的历史数据(即Date(日期), Balance(余额))。Bank 类被实例化之后,givePrediction()将被调度并作用在Bank 类实例。在此过程,根据程序运行时现金获取点的类型(ATM或Branch分行点), 将通过S4类系统自动调用ATM or Branch类的 givePrediction()方法。
代码贴上:
#定义givePrediction泛函接口 setGeneric("givePrediction", function(object) { standardGeneric("givePrediction") }) #定义Bank类,具有cashPoints属性,为list数据类型 setClass("Bank", representation(cashPoints = "list")) #定义Bank类的初始化方法initialize,读取bank_model.txt文档并为cashPoints属性赋值,注意new()函数的应用,例如,new("ATM","CashPoint_1"),意味着new一个ATM实例,第二个参数"CashPoint_1"为下文定义的CashPoint类的initialize方法的第二个参数的取值 setMethod("initialize", "Bank", function(.Object){ BankModel <- read.table(file = "bank_model.txt", sep = ";", header = TRUE, stringsAsFactors = FALSE) .Object@cashPoints <- apply(BankModel, 1, function(cp) { new(cp[2], cp[1]) }) names(.Object@cashPoints) <- apply(BankModel, 1, paste, collapse = "_") return(.Object) }) #定义Bank类的givePrediction方法,sapply函数的第二个参数值"givePrediction"将根据第一个参数cashPoints取值,调度合适的givePrediction方法(即多态) setMethod("givePrediction", "Bank", function(object){ return(sapply(object@cashPoints, "givePrediction")) }) #定义CashPoint类,虚类,具有id和balances属性,数据类型分别为字符和数据框 setClass("CashPoint", representation(id = "character", balances = "data.frame", "VIRTUAL")) #定义CashPoint类的initialize方法,应注意第二个参数cashPointId是如何取值的 setMethod("initialize", "CashPoint", function(.Object, cashPointId){ .Object@id <-cashPointId balances <- read.table(file = "branch_balances_data.txt", sep = ";", header = TRUE) .Object@balances <- subset(balances, balances$BranchId == .Object@id) .Object@balances$Date <- as.Date(.Object@balances$Date) return(.Object) }) #定义CashPoint类的givePrediction方法 setMethod("givePrediction", "CashPoint", function(object){ stop("no givePrediction method for this class") }) #定义Branch类,继承自CashPoint类,从而也具有id和balances属性,数据类型分别为字符和数据框;也具有givePrediction方法 setClass("Branch", contains = "CashPoint") #改写Branch类继承自CashPoint类的givePrediction方法,求均值以预测余额 setMethod("givePrediction", "Branch", function(object){ return(mean(object@balances$Balance)) }) #定义ATM类,继承自CashPoint类,从而也具有id和balances属性,数据类型分别为字符和数据框;也具有givePrediction方法 setClass("ATM", contains = "CashPoint") #改写ATM类继承自CashPoint类的givePrediction方法,用到了线性回归模型以预测余额 setMethod("givePrediction", "ATM", function(object) { LM <- lm(Balance ~ as.numeric(Date), data = object@balances) prediction <- predict(LM, data.frame(Date = 1 + max(object@balances$Date))) return(unname(prediction)) }) #测试 givePrediction(new("Bank"))
bank_model.txt
CashPointId;CashPointTypeCashPoint_1;ATM
CashPoint_2;Branch
CashPoint_3;ATM
CashPoint_4;Branch
CashPoint_5;ATM
branch_balances_data.txt
BranchId;Date;BalanceCashPoint_1;2012-12-01;423000
CashPoint_1;2012-12-02;312000
CashPoint_1;2012-12-03;220000
CashPoint_1;2012-12-04;123000
CashPoint_2;2012-12-01;223000
CashPoint_2;2012-12-02;212000
CashPoint_2;2012-12-03;320000
CashPoint_2;2012-12-04;223000
CashPoint_3;2012-12-01;323000
CashPoint_3;2012-12-02;312000
CashPoint_3;2012-12-03;270000
CashPoint_3;2012-12-04;223000
CashPoint_4;2012-12-01;323000
CashPoint_4;2012-12-02;412000
CashPoint_4;2012-12-03;320000
CashPoint_4;2012-12-04;373000
CashPoint_5;2012-12-01;223000
CashPoint_5;2012-12-02;192000
CashPoint_5;2012-12-03;150000
CashPoint_5;2012-12-04;133000
相关文章推荐
- .NET MSChart应用的一个简单例子
- 一个简单的Web Service应用例子
- 分享一个PHP数据流应用的简单例子
- 一个基于MINA框架应用的最简单例子
- .NET MSChart应用的一个简单例子
- 一个REST API 在Azure上应用的简单通讯录例子。(C#, MVC)
- .NET MSChart应用的一个简单例子 (转)
- Appium+Robotframework实现Android应用的自动化测试-6:一个简单的例子
- 基于MINA构建简单高性能的NIO应用-一个简单的例子
- 用R语言实现一个求导的简单例子
- ios学习笔记block回调的应用(一个简单的例子)
- 一起学libcef--一个应用libcef的简单例子(windows程序)
- 使用axis测试一个简单的webservice的应用例子
- ios学习笔记block回调的应用(一个简单的例子)
- 一起学libcef--一个应用libcef的简单例子(windows程序)
- Appium+Robotframework实现iOS应用的自动化测试-5:一个简单的例子
- Appium+Robotframework实现Android应用的自动化测试-6:一个简单的例子
- 一个基于MINA框架应用的最简单例子
- PHP数据流应用的一个简单例子