diff --git a/CHANGELOG.md b/CHANGELOG.md index 91ea334..53ba14d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ ### 0.5.3 (Upcoming release) - Add tests for GreyNumber type - methods now accept Matrix (in addition to DataFrame) - +- PIV (Proximity Indexed Value) method implemented ### 0.5.2 - Implement MEREC diff --git a/src/JMcDM.jl b/src/JMcDM.jl index bce5c03..a9329b6 100755 --- a/src/JMcDM.jl +++ b/src/JMcDM.jl @@ -102,6 +102,7 @@ include("mcdm.jl") include("copeland.jl") include("sd.jl") include("rov.jl") +include("piv.jl") @@ -136,6 +137,7 @@ import .DEMATEL: dematel, DematelResult import .Entropy: entropy, EntropyResult import .AHP: ahp, ahp_consistency, ahp_RI, AHPResult, AHPConsistencyResult import .MEREC: merec, MERECResult, MERECMethod +import .PIV: piv, PIVResult, PIVMethod import .SCDM: LaplaceResult, MaximinResult, MaximaxResult, MinimaxResult, MiniminResult import .SCDM: SavageResult, HurwiczResult, MLEResult, ExpectedRegretResult @@ -174,6 +176,7 @@ export PSIMethod export MoosraMethod export ROVMethod export MERECMethod +export PIVMethod export MCDMSetting @@ -206,6 +209,7 @@ export ROVResult export PSIResult export MoosraResult export MERECResult +export PIVResult # export game type export GameResult @@ -264,6 +268,7 @@ export rov export psi export moosra export merec +export piv #  export SCDM tools export laplace diff --git a/src/codas.jl b/src/codas.jl index a13e898..162f45b 100644 --- a/src/codas.jl +++ b/src/codas.jl @@ -30,6 +30,8 @@ function Base.show(io::IO, result::CODASResult) println(io, "Best indices:") println(io, result.bestIndex) end + + """ codas(decisionMat, weights, fs) Apply CODAS (COmbinative Distance-based ASsessment) method for a given matrix, weights and, type of criteria. diff --git a/src/piv.jl b/src/piv.jl new file mode 100644 index 0000000..f4a84b8 --- /dev/null +++ b/src/piv.jl @@ -0,0 +1,91 @@ +module PIV + +export piv, PIVResult, PIVMethod + +import ..MCDMMethod, ..MCDMResult, ..MCDMSetting +using ..Utilities + +using DataFrames + +struct PIVResult <: MCDMResult + decisionMatrix::DataFrame + normalizedMatrix::DataFrame + weightedNormalizedMatrix::DataFrame + w::Array{Float64,1} + scores::Vector + ranking::Array{Int64,1} + bestIndex::Int64 +end + +struct PIVMethod <: MCDMMethod +end + +function Base.show(io::IO, result::PIVResult) + println(io, "Scores:") + println(io, result.scores) + println(io, "Ordering: ") + println(io, result.ranking) + println(io, "Best index:") + println(io, result.bestIndex) +end + +""" + piv(decisionMat, weights, fs) +Apply PIV (Proximity Indexed Value) method for a given matrix, weights and, type of criteria. + +# Arguments: + - `decisionMat::DataFrame`: n × m matrix of objective values for n alternatives and m criteria + - `weights::Array{Float64, 1}`: m-vector of weights that sum up to 1.0. If the sum of weights is not 1.0, it is automatically normalized. + - `fs::Array{Function,1}`: m-vector of type of criteria. The benefit criteria shown with "maximum", and the cost criteria shown with "minimum". + + # Description +piv() applies the PIV method to rank n alternatives subject to m criteria and criteria type vector. Alternatives +with lesser scores values (u_i values in the original article) are better as they represent the deviation +from the ideal values. + + +# Output +- `::PIVResult`: PIVResult object that holds multiple outputs including scores, rankings, and best index. + +# References +Sameera Mufazzal, S.M. Muzakkir, A new multi-criterion decision making (MCDM) method based on proximity indexed value for minimizing rank reversals, +Computers & Industrial Engineering, Volume 119, 2018, Pages 427-438, ISSN 0360-8352, +https://doi.org/10.1016/j.cie.2018.03.045. +""" +function piv(decisionMat::DataFrame, weights::Array{Float64,1}, fns::Array{Function,1})::PIVResult + + normalized_dec_mat = Utilities.normalize(decisionMat) + weighted_norm_mat = weights * normalized_dec_mat + + nrow, ncol = size(weighted_norm_mat) + + desiredvalues = Utilities.apply_columns(fns, weighted_norm_mat) + + finalmat = similar(weighted_norm_mat) + + @inbounds for i in 1:nrow + for j in 1:ncol + if fns[j] == maximum + finalmat[i,j] = desiredvalues[j] - weighted_norm_mat[i,j] + elseif fns[j] == minimum + finalmat[i,j] = weighted_norm_mat[i,j] - desiredvalues[j] + end + end + end + + di = Utilities.apply_rows(sum, finalmat) + + ranks = di |> sortperm + bestIndex = ranks |> first + + return PIVResult( + decisionMat, + normalized_dec_mat, + weighted_norm_mat, + weights, + di, + ranks, + bestIndex) +end + +end # End of Module PIV \ No newline at end of file diff --git a/test.jl b/test.jl index f7dbc7d..1a42b1c 100644 --- a/test.jl +++ b/test.jl @@ -1,18 +1,3 @@ using JMcDM -df = DataFrame( - :K1 => [105000.0, 120000, 150000, 115000, 135000], - :K2 => [105.0, 110, 120, 105, 115], - :K3 => [10.0, 15, 12, 20, 15], - :K4 => [4.0, 4, 3, 4, 5], - :K5 => [300.0, 500, 550, 600, 400], - :K6 => [10.0, 8, 12, 9, 9], - ) - functionlist = [minimum, maximum, minimum, maximum, maximum, minimum] - w = [0.05, 0.20, 0.10, 0.15, 0.10, 0.40] - - gdf = makegrey(Matrix(df)) - result = aras(makeDecisionMatrix(gdf), w, functionlist) - - @show result diff --git a/test/testmcdm.jl b/test/testmcdm.jl index 3036d0e..f48e7e0 100644 --- a/test/testmcdm.jl +++ b/test/testmcdm.jl @@ -1343,5 +1343,33 @@ @test result2 isa MERECResult @test result2.w == result.w end + + @testset "PIV" begin + tol = 0.0001 + + decmat = [ + 60 2.5 2540 500 990 + 6.35 6.667 1016 3000 1041 + 6.8 10 1727 1500 1676 + 10 5 1000 2000 965 + 2.5 9.8 560 500 915 + 4.5 12.5 1016 350 508 + 3 10 1778 1000 920] + + w = [0.1761, 0.2042, 0.2668, 0.1243, 0.2286] + + fns = [maximum, maximum, maximum, maximum, maximum] + + result = piv(makeDecisionMatrix(decmat), w, makeminmax(fns)) + + @test result isa PIVResult + @test isapprox( + result.scores, + [0.22086675609968962, 0.35854940101222144, 0.2734184099704686, 0.4005382183676046, 0.4581157878699193, 0.43595371718873477, 0.3580651803072459], + atol = tol, + ) + @test result.bestIndex == 1 + @test result.ranking == [1, 3, 7, 2, 4, 6, 5] + end end