Nothing
# $Id: dea.R 241 2022-03-16 14:00:23Z X052717 $
# DEA beregning via brug af lp_solveAPI. Fordelene ved lp_solveAPI er
# faerre kald fra R med hele matricer for hver unit og dermed skulle
# det gerne vaere en hurtigere metode. Maaske er det ogsaa lettere at
# gennemskue hvad der bliver gjort en gang for alle og hvad der bliver
# aendret ved beregning for hver unit.
# Option FAST=TRUE giver en meget hurtigere beregning af efficienser,
# men tilgaengaeld bliver der IKKE gemt de beregnede lambdaer. Det
# betyder bl.a. at der ikke kan findes peers for de enkelte units.
dea <- function(X,Y, RTS="vrs", ORIENTATION="in", XREF=NULL,YREF=NULL,
FRONT.IDX=NULL, SLACK=FALSE, DUAL=FALSE, DIRECT=NULL, param=NULL,
TRANSPOSE=FALSE, FAST=FALSE, LP=FALSE, CONTROL=NULL, LPK=NULL)
{
# XREF, YREF determines the technology
# FRONT.IDX index for units that determine the technology
# In the calculation in the method input/output matrices X and Y
# are of the order good x units.
# TRANSPOSE the restriction matrix is transposed, For TRUE then X and Y are
# matrices of dimension inputs/ouputs times number of units, i.e.
# goods are rows, and therefore X, Y etc must be transformed
# as default in R is unit x good.
if ( FAST ) {
DUAL=FALSE; # SLACK=FALSE;
# print("When FAST then neither DUAL nor SLACK")
}
#----------
# Tjek af RTS og ORIENTATION
rts <- c("fdh","vrs","drs","crs","irs","irs2","add","fdh+","fdh++","fdh0", "vrs+")
if ( missing(RTS) ) RTS <- "vrs"
if ( is.numeric(RTS) ) {
if (LP) print(paste("Number '",RTS,"'",sep=""),quote=F)
RTStemp <- rts[1+RTS] # the first, fdh, is number 0
RTS <- RTStemp
if (LP) print(paste("' is '",RTS,"'\n",sep=""),quote=F)
}
RTS <- tolower(RTS)
if ( !(RTS %in% rts) ) stop("Unknown scale of returns:", RTS)
orientation <- c("in-out","in","out","graph")
if ( is.numeric(ORIENTATION) ) {
ORIENTATION_ <- orientation[ORIENTATION+1] # "in-out" er nr. 0
ORIENTATION <- ORIENTATION_
}
ORIENTATION <- tolower(ORIENTATION)
if ( !(ORIENTATION %in% orientation) ) {
stop("Unknown value for ORIENTATION:",ORIENTATION)
}
#----------
# Tjek af input
# Hvis data er en data.frame saa tjek om det er numerisk data og lav
# dem i saa fald om til en matrix
X <- tjek_data(X)
Y <- tjek_data(Y)
.xyref.missing <- FALSE
if ( missing(XREF) || is.null(XREF) ) {
.xyref.missing <- TRUE
XREF <- X
}
if ( missing(YREF) || is.null(YREF) ) {
.xyref.missing <- TRUE && .xyref.missing
YREF <- Y
}
XREF <- tjek_data(XREF)
YREF <- tjek_data(YREF)
if ( TRANSPOSE ) {
X <- t(X)
Y <- t(Y)
XREF <- t(XREF)
YREF <- t(YREF)
if ( !is.null(DIRECT) & is(DIRECT, "matrix") )
DIRECT <- t(DIRECT)
}
#------------
# Tjek af dimensioner
orgKr <- dim(XREF)
if ( length(FRONT.IDX) > 0 ) {
if (LP) print("FRONT.IDX")
if (LP) print(FRONT.IDX)
if ( !is.vector(FRONT.IDX) & !(ifelse(is.matrix(FRONT.IDX), dim(FRONT.IDX)[2]==1, FALSE) ))
stop("FRONT.IDX is not a vector or collumn matrix in 'dea'")
XREF <- XREF[FRONT.IDX,, drop=FALSE]
YREF <- YREF[FRONT.IDX,, drop=FALSE]
}
rNames <- rownames(XREF)
if ( is.null(rNames) & !is.null(rownames(YREF)) )
rNames <- rownames(YREF)
if ( is.null(rNames) )
rNames <- 1:dim(XREF)[1]
m <- dim(X)[2] # number of inputs
n <- dim(Y)[2] # number of outputs
K <- dim(X)[1] # number of units, units, DMUs
Ky <- dim(Y)[1]
Kr <- dim(XREF)[1] # number of units,units in the reference technology
oKr <- orgKr[1] # number of units in reference before use of FRONT.IDX
if ( !is.null(DIRECT) ) {
if ( is(DIRECT, "matrix") ) {
md <- dim(DIRECT)[2]
Kd <- dim(DIRECT)[1]
} else {
md <- length(DIRECT)
Kd <- 0
}
} else {
Kd <- 0
}
if (LP) cat("m n K Kr = ",m,n,K,Kr,"\n")
if (LP & !is.null(DIRECT) ) cat("md, Kd =",md,Kd,"\n")
if (m!=dim(XREF)[2]) stop("Number of inputs must be the same in X and XREF")
if (n!=dim(YREF)[2]) stop("Number of outputs must be the same in Y and YREF")
if (K!=Ky) stop("Number of units must be the same in X and Y")
if (Kr!=dim(YREF)[1]) stop("Number of units must be the same in XREF and YREF")
if ( !is.null(DIRECT) & all(DIRECT=="min") & ORIENTATION=="graph" )
# Kaldet kommer fra 'mea' og stop vil vise 'dea' kaldet som mea laver; derfor call.=FALSE
stop("The option 'ORIENTATION=\"graph\"' cannot be used for 'mea'", call.=FALSE)
if ( !is.null(DIRECT) & length(DIRECT) > 1 ) {
if ( ORIENTATION=="in" & md!=m )
stop("Length of DIRECT must be the number of inputs")
else if ( ORIENTATION=="out" & md!=n )
stop("Length of DIRECT must be the number of outputs")
else if ( ORIENTATION=="in-out" & md!=m+n )
stop("Length of DIRECT must be the number of inputs plus outputs")
if ( is(DIRECT, "matrix") & (Kd>0 & Kd!=K) )
stop("Number of units in DIRECT must equal units in X and Y")
}
if ( !is.null(DIRECT) & length(DIRECT) == 1 ) {
if ( ORIENTATION=="in" & length(DIRECT)!=m )
DIRECT <- rep(DIRECT,m)
else if ( ORIENTATION=="out" & length(DIRECT)!=n )
DIRECT <- rep(DIRECT,n)
else if ( ORIENTATION=="in-out" & length(DIRECT)!=m+n )
DIRECT <- rep(DIRECT,m+n)
}
#------------
if ( RTS=="fdh" && ORIENTATION!="graph" && !FAST && DUAL==FALSE
&& all(DIRECT!="min") ) {
e <- fdh(X,Y, ORIENTATION=ORIENTATION, XREF=XREF, YREF=YREF,
FRONT.IDX=FRONT.IDX, DIRECT=DIRECT, TRANSPOSE=FALSE, oKr)
if ( SLACK ) {
warning("Run 'slack(X, Y, e)' to get slacks")
}
return(e)
}
#------------
if ( RTS=="fdh++" ) {
e <- dea.fdhPlus(X, Y, ORIENTATION=ORIENTATION,
XREF=XREF, YREF=YREF, FRONT.IDX=FRONT.IDX, DIRECT=DIRECT,
param=param, TRANSPOSE=FALSE, oKr)
if ( SLACK ) {
warning("Run 'slack(X, Y, e)' to get slacks")
}
return(e)
}
#------------
if ( RTS != "crs" && RTS != "add" ) {
rlamb <- 2
} else {
rlamb <- 0
}
#------------
# Lav LP objekt og saet alt der ikke aendres ved ny firm.
# Initialiser LP objekt
lps <- make.lp(m+n +rlamb,1+Kr)
# Saet lp options
lp.control(lps,
scaling=c("range", "equilibrate", "integers") # default scalering er 'geometric'
) # og den giver ikke altid tilfredsstillende resultat;
# curtisreid virker i mange tilfaelde slet ikke
if ( is.null(DIRECT) ) dirStreng<-"" else dirStreng<-"Dir"
name.lp(lps, paste(ifelse(is.null(DIRECT)|(DIRECT!="min"), "Dea", "Mea"),
ORIENTATION,RTS,dirStreng,sep="-"))
if (!missing(CONTROL)) set_control(lps, CONTROL)
# saet raekker i matrix med restriktioner, saet 0'er for den foerste
# soejle for den skal alligevel aendres for hver unit.
# Bemaerk X og Y transponeres implicit naar de saettes i lp_solveAPI;
# soejler i X og Y saettes som raekker i lp_solveAPI.
# Foerste 'm' raekker med input
for ( h in 1:m )
set.row(lps,h, c(0,-XREF[,h]))
# Foelgende 'n' raekker med output
for ( h in 1:n)
set.row(lps,m+h, c(0,YREF[,h]))
# restriktioner paa lambda
if ( RTS != "crs" && RTS != "add" ) {
set.row(lps, m+n+1, c(0,rep(-1,Kr)))
set.row(lps, m+n+2, c(0,rep( 1,Kr)))
}
# Saet restriktioner for lambda, dvs. for RTS
if ( RTS == "fdh" || RTS == "fdh0" ) {
set.type(lps,2:(1+Kr),"binary")
set.rhs(lps,-1, m+n+1)
delete.constraint(lps, m+n+2)
rlamb <- rlamb -1
} else if ( RTS == "vrs" ) {
set.rhs(lps, c(-1,1), (m+n+1):(m+n+2))
} else if ( RTS == "vrs+" ) {
# param: (lav, hoej, sum lav, sum hoej)
# Saet parametrene low og high
if ( is.null(param) ) {
param <- c(.5, 2.)
}
if ( length(param) == 1 ) {
low <- param
high <- 1+(1-param)
} else {
low <- param[1]
high <- param[2]
}
if (length(param) == 4) {
# print("Graenser for sum af lambda sat")
set.rhs(lps, c(-param[4], param[3]), (m+n+1):(m+n+2))
} else {
set.rhs(lps, c(-1,1), (m+n+1):(m+n+2))
}
param <- c(low=low, high=high)
set.semicont(lps, 2:(1+Kr))
set.bounds(lps, lower=rep(low,Kr), upper=rep(high,Kr), columns=2:(1+Kr))
} else if ( RTS == "drs" ) {
set.rhs(lps, -1, m+n+1)
delete.constraint(lps, m+n+2)
rlamb <- rlamb -1
} else if ( RTS == "irs" ) {
set.rhs(lps, 1, m+n+2)
delete.constraint(lps, m+n+1)
rlamb <- rlamb -1
} else if ( RTS == "irs2" ) {
set.rhs(lps, 1, m+n+2)
delete.constraint(lps, m+n+1)
rlamb <- rlamb -1
set.semicont(lps, 2:(1+Kr))
set.bounds(lps, lower=rep(1,Kr), columns=2:(1+Kr))
} else if ( RTS == "add" ) {
set.type(lps,2:(1+Kr),"integer")
} else if ( RTS == "fdh+" ) {
# Saet parametrene low og high
if ( is.null(param) ) {
param <- .15
}
if ( length(param) == 1 ) {
low <- 1-param
high <- 1+param
} else {
low <- param[1]
high <- param[2]
}
param <- c(low=low, high=high)
set.rhs(lps, c(-high, low), (m+n+1):(m+n+2))
add.SOS(lps,"lambda", 1,1, 2:(1+Kr), rep(1, Kr))
}
if ( !is.null(DIRECT) & Kd<=1 & all(DIRECT!="min") ) {
# print(Kd)
# print(DIRECT)
# Samme retning for alle enheder
if ( ORIENTATION=="in" )
set.column(lps, 1, c(1,-DIRECT),0:m)
else if ( ORIENTATION=="out" )
set.column(lps, 1, c(1,-DIRECT),c(0,(m+1):(m+n)))
else if ( ORIENTATION=="in-out" )
set.column(lps, 1, c(1,-DIRECT),0:(m+n))
}
if ( !is.null(DIRECT) ) {
# Ved super efficiency for directional kan loesning vaere
# negativ saa loensingen skal kunne vaere negativ, dvs. objval
# kan vaere negativ.
set.bounds(lps,lower=-Inf, columns=1)
}
set.objfn(lps, 1,1)
# Baade in- og output modeller skal formuleres med ">="
set.constr.type(lps, rep(">=",m+n+rlamb))
if ( ORIENTATION %in% c("in","graph") ) {
lp.control(lps, sense="min")
} else if ( ORIENTATION == "out" ) {
lp.control(lps, sense="max")
} else if ( ORIENTATION == "in-out" & !is.null(DIRECT) ) {
lp.control(lps, sense="max")
} else
stop("In 'dea' for ORIENTATION use only 'in', 'out', 'graph', or 'in-out (only for DIRECT)")
# Ved brug af directional efficiency er der altid tale om et max-problem
if ( !is.null(DIRECT) ) {
lp.control(lps, sense="max")
}
if ( ORIENTATION == "graph" ) {
oe <- graphEff(lps, X, Y, XREF, YREF, RTS, FRONT.IDX, rlamb, oKr,
param=param, TRANSPOSE, SLACK, FAST, LP)
# delete.lp(lps)
return(oe)
}
objval <- rep(NA,K) # vector for the final efficiencies
if ( FAST ) {
lambda <- NULL
primal <- NULL
dual <- NULL
} else {
lambda <- matrix(NA, nrow=K, ncol=Kr) # lambdas one column per unit
rownames(lambda) <- rownames(X)
colnames(lambda) <- rNames
if (DUAL) {
dual <- matrix(NA, nrow=K, ncol=sum(dim(lps))+1) #
primal <- matrix(NA, nrow=K, ncol=sum(dim(lps))+1) # solutions
rownames(dual) <- rownames(X)
rownames(primal) <- rownames(X)
} else {
primal <- NULL
dual <- NULL
}
}
if ( !is.null(DIRECT) & all(DIRECT == "min") ) {
directMin <- TRUE
if ( ORIENTATION=="in" ) {
directMatrix <- matrix(NA, nrow=K, ncol=m)
} else if ( ORIENTATION=="out" ) {
directMatrix <- matrix(NA, nrow=K, ncol=n)
} else if ( ORIENTATION=="in-out" ) {
directMatrix <- matrix(NA, nrow=K, ncol=m+n)
}
} else {
directMin <- FALSE
}
# The loop for each unit
for ( k in 1:K) {
if ( LP ) print(paste("Unit",k," -------------------"), quote=FALSE)
# Af en eller anden grund saetter set.column ogsaa vaerdi for
# kriteriefunktion og hvis der ikke er nogen vaerdi bliver den
# automatisk sat til 0. Derfor maa 1-tallet for
# kriteriefunktionen med for denne soejle og det er raekke 0.
if ( directMin ) {
# Saet hoejreside for enhedens input og output
set.rhs(lps, c(-X[k,],Y[k,]), 1:(m+n))
# Find retningen og saet foerste soejle til den
if ( ORIENTATION=="in" ) {
DIRECT <- minDirection(lps, m, n, ORIENTATION, LP=LP)
set.column(lps, 1, c(1,-DIRECT),0:m)
} else if ( ORIENTATION=="out" ) {
DIRECT <- minDirection(lps, m, n, ORIENTATION, LP=LP)
set.column(lps, 1, c(1,-DIRECT), c(0,(m+1):(m+n)) )
} else if ( ORIENTATION=="in-out" ) {
DIRECT <- minDirection(lps, m, n, ORIENTATION)
set.column(lps, 1, c(1,-DIRECT),0:(m+n))
}
directMatrix[k,] <- DIRECT
if (LP) { print("Min DIRECT:"); print(DIRECT) }
# Check om DIRECT er 0, hvis den er nul gaa til naeste unit
lpcontr <- lp.control(lps)
eps <- sqrt(lpcontr$epsilon["epsint"])
if (LP) print(paste("eps for minDirectin",eps), quote=FALSE)
# Daarligt test for om direction er 0, tager ikke hensyn at X'er og
# Y'er indbyrdes kan vaere forskellig stoerrelse
if ( ORIENTATION=="in" ) {
deltaDir <- DIRECT/( X[k,] + .Machine$double.xmin )
} else if ( ORIENTATION=="out" ) {
deltaDir <- DIRECT/( Y[k,] + .Machine$double.xmin )
} else if ( ORIENTATION=="in-out" ) {
deltaDir <- DIRECT/( c(X[k,],Y[k,]) + .Machine$double.xmin )
}
if ( max(DIRECT) < eps && max(abs(deltaDir)) < eps )
{
if (LP) print(paste("Direction 0 for unit",k))
objval[k] <- 0
if ( !FAST ) {
lambda[k,] <- rep(0,Kr)
lambda[k,k] <- 1
}
next # ingen direction at gaa, tag naeste unit
}
} # if ( directMin )
if ( is.null(DIRECT) ) {
if ( ORIENTATION == "in" ) {
set.column(lps, 1, c(1,X[k,]),0:m)
set.rhs(lps, Y[k,], (m+1):(m+n))
} else {
set.column(lps, 1, c(1,-Y[k,]),c(0,(m+1):(m+n)))
set.rhs(lps, -X[k,], 1:m)
}
} else {
# print(Kd)
# print(DIRECT)
set.rhs(lps, c(-X[k,],Y[k,]), 1:(m+n))
if ( Kd > 1 ) {
# retning for enheden
if ( ORIENTATION=="in" )
set.column(lps, 1, c(1,-DIRECT[k,]),0:m)
else if ( ORIENTATION=="out" )
set.column(lps, 1, c(1,-DIRECT[k,]), c(0,(m+1):(m+n)) )
else if ( ORIENTATION=="in-out" )
set.column(lps, 1, c(1,-DIRECT[k,]),0:(m+n))
}
}
if ( LP && k <= 10 ) print(lps)
set.basis(lps, default=TRUE)
status <- solve(lps)
if ( status == 5 ) {
# Numerical failure, reset basis og proev igen evt. med en
# anden skalering; desvaerre virker det helt forkerte resultater
# at aendre skalering, dvs. at bruge 'dynupdate'.
set.basis(lps, default=TRUE)
status <- solve(lps)
}
if (LP) print(paste("Status =",status))
if ( status != 0 ) {
if ( status == 2 || status == 3 ) {
# At unit ikke er i teknologimaengden svarer til 'Inf', det
# er derfor unoedvendigt at give en advarsel.
#cat("Unit ", k, " is not in the technology set. Status = ", status,
# "\n", sep="")
objval[k] <- ifelse(ORIENTATION=="in", Inf, -Inf)
} else {
cat("Error in solving for unit ", k, ": Status =",status, "\n", sep="")
objval[k] <- NA
}
sol <- NA
} else {
objval[k] <- get.objective(lps)
if ( !FAST ) sol <- get.variables(lps)
}
if ( !FAST ) {
lambda[k,] <- sol[2:(1+Kr)]
if ( DUAL ) {
primal[k,] <- get.primal.solution(lps)
dual[k,] <- get.dual.solution(lps)
}
}
if (LP && status==0) {
print(paste("Objval, unit",k))
print(get.objective(lps))
print("Solution/variables")
print(get.variables(lps))
print("Primal solution")
print(get.primal.solution(lps))
print("Dual solution:")
print(get.dual.solution(lps))
}
if ( !is.null(LPK) && k %in% LPK ) {
write.lp(lps, paste(name.lp(lps),k,".mps",sep=""),
type="mps",use.names=TRUE)
}
} # loop for each unit
e <- objval
lpcontr <- lp.control(lps)
eps <- lpcontr$epsilon["epsint"]
e[abs(e-1) < eps] <- 1 # 'e' taet ved 1 skal vaere 1
if ( !is.null(dimnames(X)[[1]]) ) {
names(e) <- dimnames(X)[[1]]
}
lambda[abs(lambda-1) < eps] <- 1 # taet ved 1
lambda[abs(lambda) < eps] <- 0 # taet ved 0
# Faerdig med at bruge lps
# delete.lp(lps)
# if ( ORIENTATION == "in" ) {
# names(e) <- "E"
# } else if ( ORIENTATION == "out" ) {
# names(e) <- "F"
# } else if ( ORIENTATION == "graph" ) {
# names(e) <- "G"
# }
if ( FAST ) {
return(e)
stop("Her skulle vi ikke kunne komme i 'dea'")
}
if (LP) print("Forbi retur fra FAST")
if ( is.null(rownames(lambda)) ) {
if ( length(FRONT.IDX)>0 ) {
colnames(lambda) <- paste("L",(1:oKr)[FRONT.IDX],sep="")
} else {
colnames(lambda) <- paste("L",1:Kr,sep="")
}
} else {
colnames(lambda) <- paste("L",rNames,sep="_")
}
if ( DUAL ) {
if ( ORIENTATION == "out" ) sign <- -1 else sign <- 1
ux <- sign*dual[,2:(1+m),drop=FALSE]
vy <- sign*dual[,(2+m):(1+m+n),drop=FALSE]
colnames(ux) <- paste("u",1:m,sep="")
colnames(vy) <- paste("v",1:n,sep="")
if ( rlamb > 0 )
gamma <- dual[,(1+m+n+1):(1+m+n+rlamb),drop=FALSE]
else
gamma <- NULL
} else {
ux <- vy <- NULL
} ## if (DUAL)
if (LP) print("DUAL faerdig")
if ( directMin ) {
DIRECT <- directMatrix
}
if ( TRANSPOSE ) {
if ( is(e, "matrix") )
e <- t(e)
lambda <- t(lambda)
if (DUAL) {
ux <- t(ux)
vy <- t(vy)
primal <- t(primal)
dual <- t(dual)
if ( !is.null(gamma) ) gamma <- t(gamma)
}
if ( !is.null(DIRECT) & is(DIRECT, "matrix") )
DIRECT <- t(DIRECT)
} ## if (TRANSPOSE)
oe <- list(eff=e, lambda=lambda, objval=objval, RTS=RTS,
primal=primal, dual=dual, ux=ux, vy=vy, gamma=gamma,
ORIENTATION=ORIENTATION, TRANSPOSE=TRANSPOSE,
# slack=NULL, sx=NULL, sy=NULL, sum=NULL,
param=param
)
if (!is.null(DIRECT)) {
oe$direct <- DIRECT
}
class(oe) <- "Farrell"
if ( SLACK ) {
if ( TRANSPOSE ) { # Transponer tilbage hvis de blev transponeret
X <- t(X)
Y <- t(Y)
if (.xyref.missing) {
XREF <- NULL
YREF <- NULL
} else {
XREF <- t(XREF)
YREF <- t(YREF)
}
}
sl <- slack(X, Y, oe, XREF, YREF, FRONT.IDX, LP=LP)
oe$slack <- sl$slack
oe$sum <- sl$sum
oe$sx <- sl$sx
oe$sy <- sl$sy
oe$lambda <- sl$lambda
if (LP) {
print("slack fra slack:")
print(sl$slack)
print("slack efter slack:")
print(oe$slack)
}
} ## if (SLACK)
return(oe)
} # dea
# Kontrol af om data er numerisk
data_kontrol <- function(X) {
if ( is.null(X) ) return(TRUE)
if (!is.data.frame(X) && !is.numeric(X)) return(FALSE)
if ( is.data.frame(X)) {
nc <- dim(X)[2]
for ( i in 1:nc ) {
if ( !is.numeric(X[,i]) ) {
return(FALSE)
break
}
}
}
return(TRUE)
} # data_kontrol
# Tjek om data er matrix og hvis ikke lav om til matrix
tjek_data <- function(X) {
if (is.matrix(X) & is.numeric(X)) return(X)
if (is.null(X) || !data_kontrol(X)) {
stop("'", deparse(substitute(X)),
"' is not a numeric matrix (or numeric data.frame)", call.=FALSE)
}
if (!is.data.frame(X) || !is.numeric(X)) {
X <- as.matrix(X)
}
return(X)
} # tjek_data
# Saet option til lpsolveAPI; isaer relevant for skalering
set_control <- function(lp, CONTROL=NULL)
{
if ( !is.null(CONTROL) ) {
if( !is.list(CONTROL)) {
stop( "argument 'CONTROL' must be a 'list' object")
}
do.call( lp.control, c( list( lprec = lp ), CONTROL ) )
}
return(NULL)
} # set_control
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.