Skip to content

Commit

Permalink
implement PSI (Preference Selection Index) method
Browse files Browse the repository at this point in the history
  • Loading branch information
jbytecode committed May 22, 2022
1 parent 04af3f0 commit 0414da9
Show file tree
Hide file tree
Showing 9 changed files with 211 additions and 14 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
### 0.3.9
- Implement PSI (Preference Selection Index) method

### 0.3.8
- Small bug fixes
- Remove the critic method from summary()
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "JMcDM"
uuid = "358108f5-d052-4d0a-8344-d5384e00c0e5"
authors = ["Mehmet Hakan Satman (jbytecode) <[email protected]>", "Bahadir Fatih Yildirim <[email protected]>", "Ersagun Kuruca"]
version = "0.3.8"
version = "0.3.9"

[deps]
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ Please check out the reference manual [here](https://jbytecode.github.io/JMcDM/d
- Copeland (For combining multiple ordering results)
- SD Method for determining weights of criteria
- ROV (Range of Value) Method
- PSI (Preference Selection Index) Method

### SCDM Tools

Expand Down
4 changes: 4 additions & 0 deletions src/JMcDM.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ include("cocoso.jl")
include("critic.jl")
include("entropy.jl")
include("codas.jl")
include("psi.jl")

include("summary.jl")

Expand Down Expand Up @@ -72,6 +73,7 @@ export VikorMethod
export WPMMethod
export WaspasMethod
export MarcosMethod
export PSIMethod

export MCDMSetting

Expand Down Expand Up @@ -101,6 +103,7 @@ export EntropyResult
export CODASResult
export SDResult
export ROVResult
export PSIResult

# export game type
export GameResult
Expand Down Expand Up @@ -155,6 +158,7 @@ export entropy
export codas
export sd
export rov
export psi

#  export SCDM tools
export laplace
Expand Down
34 changes: 21 additions & 13 deletions src/print.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ end
function Base.show(io::IO, result::CODASResult)
println(io, "Scores:")
println(io, result.scores)
println(io, "Ranking: ")
println(io, "Ordering: ")
println(io, result.ranking)
println(io, "Best indices:")
println(io, result.bestIndex)
Expand All @@ -32,14 +32,14 @@ end
function Base.show(io::IO, result::COPRASResult)
println(io, "Scores:")
println(io, result.scores)
println(io, "Ranking: ")
println(io, "Ordering: ")
println(io, result.ranking)
println(io, "Best indices:")
println(io, result.bestIndex)
end

function Base.show(io::IO, result::CRITICResult)
println(io, "Ranking: ")
println(io, "Ordering: ")
println(io, result.ranking)
println(io, "Best indices:")
println(io, result.bestIndex)
Expand All @@ -48,7 +48,7 @@ end
function Base.show(io::IO, result::CoCoSoResult)
println(io, "Scores:")
println(io, result.scores)
println(io, "Ranking: ")
println(io, "Ordering: ")
println(io, result.ranking)
println(io, "Best indices:")
println(io, result.bestIndex)
Expand All @@ -57,7 +57,7 @@ end
function Base.show(io::IO, result::EDASResult)
println(io, "Scores:")
println(io, result.scores)
println(io, "Ranking: ")
println(io, "Ordering: ")
println(io, result.ranking)
println(io, "Best indices:")
println(io, result.bestIndex)
Expand All @@ -80,7 +80,7 @@ end
function Base.show(io::IO, result::MABACResult)
println(io, "Scores:")
println(io, result.scores)
println(io, "Ranking: ")
println(io, "Ordering: ")
println(io, result.ranking)
println(io, "Best indices:")
println(io, result.bestIndex)
Expand All @@ -89,7 +89,7 @@ end
function Base.show(io::IO, result::MAIRCAResult)
println(io, "Scores:")
println(io, result.scores)
println(io, "Ranking: ")
println(io, "Ordering: ")
println(io, result.ranking)
println(io, "Best indices:")
println(io, result.bestIndex)
Expand All @@ -98,7 +98,7 @@ end
function Base.show(io::IO, result::MARCOSResult)
println(io, "Scores:")
println(io, result.scores)
println(io, "Ranking: ")
println(io, "Ordering: ")
println(io, result.ranking)
println(io, "Best indices:")
println(io, result.bestIndex)
Expand All @@ -114,7 +114,7 @@ end
function Base.show(io::IO, result::PrometheeResult)
println(io, "Scores:")
println(io, result.scores)
println(io, "Ranking: ")
println(io, "Ordering: ")
println(io, result.ranking)
println(io, "Best indices:")
println(io, result.bestIndex)
Expand All @@ -123,7 +123,7 @@ end
function Base.show(io::IO, result::SawResult)
println(io, "Scores:")
println(io, result.scores)
println(io, "Ranking: ")
println(io, "Ordering: ")
println(io, result.ranking)
println(io, "Best indices:")
println(io, result.bestIndex)
Expand All @@ -139,7 +139,7 @@ end
function Base.show(io::IO, result::WASPASResult)
println(io, "Scores:")
println(io, result.scores)
println(io, "Ranking: ")
println(io, "Ordering: ")
println(io, result.ranking)
println(io, "Best indices:")
println(io, result.bestIndex)
Expand All @@ -148,7 +148,7 @@ end
function Base.show(io::IO, result::WPMResult)
println(io, "Scores:")
println(io, result.scores)
println(io, "Ranking: ")
println(io, "Ordering: ")
println(io, result.ranking)
println(io, "Best indices:")
println(io, result.bestIndex)
Expand All @@ -157,14 +157,22 @@ end
function Base.show(io::IO, result::ROVResult)
println(io, "Scores:")
println(io, result.scores)
println(io, "Ranking: ")
println(io, "Ordering: ")
println(io, result.ranks)
println(io, "Uminus:")
println(io, result.uminus)
println(io, "UPlus:")
println(io, result.uplus)
end

function Base.show(io::IO, result::PSIResult)
println(io, "Scores:")
println(io, result.scores)
println(io, "Ordering: (from worst to best)")
println(io, result.rankings)
println(io, "Best indices:")
println(io, result.bestIndex)
end



131 changes: 131 additions & 0 deletions src/psi.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
"""
psi(decisionMat, fns)
Apply PSI (Preference Selection Index) method for a given matrix and directions of optimizations.
# Arguments:
- `decisionMat::DataFrame`: n × m matrix of objective values for n alterntives and m criteria
- `fns::Array{Function, 1}`: m-vector of functions to be applied on the columns.
# Description
psi() applies the PSI method to rank n alterntives subject to m criteria which are supposed to be
either maximized or minimized.
# Output
- `::PSIResult`: PSIResult object that holds multiple outputs including scores, rankings, and best index.
# Examples
```julia-repl
julia> decmat = [3 12.5 2 120 14 3;
5 15 3 110 38 4;
3 13 2 120 19 3;
4 14 2 100 31 4;
3 15 1.5 125 40 4]
5×6 Array{Float64,2}:
3.0 12.5 2.0 120.0 14.0 3.0
5.0 15.0 3.0 110.0 38.0 4.0
3.0 13.0 2.0 120.0 19.0 3.0
4.0 14.0 2.0 100.0 31.0 4.0
3.0 15.0 1.5 125.0 40.0 4.0
julia> df = makeDecisionMatrix(decmat)
5×6 DataFrame
Row │ Crt1 Crt2 Crt3 Crt4 Crt5 Crt6
│ Float64 Float64 Float64 Float64 Float64 Float64
─────┼──────────────────────────────────────────────────────
1 │ 3.0 12.5 2.0 120.0 14.0 3.0
2 │ 5.0 15.0 3.0 110.0 38.0 4.0
3 │ 3.0 13.0 2.0 120.0 19.0 3.0
4 │ 4.0 14.0 2.0 100.0 31.0 4.0
5 │ 3.0 15.0 1.5 125.0 40.0 4.0
julia> fns = [maximum, minimum, minimum, maximum, minimum, maximum];
julia> result = psi(df, fns)
Scores:
[1.1252480520930113, 0.762593438114615, 1.1060476892230147, 1.0059872302387025, 0.7865885089329105]
Ordering: (from worst to best)
[2, 5, 4, 3, 1]
Best indices:
1
julia> result.bestIndex
1
```
# References
Maniya, Kalpesh, and Mangal Guido Bhatt. "A selection of material using a novel type decision-making method:
Preference selection index method." Materials & Design 31.4 (2010): 1785-1789
"""
function psi(decisionMat::DataFrame, fns::Array{Function,1})::PSIResult

function PV(v)
mymean = mean(v)
meandiff = v .- mymean
meandiffsq = meandiff .* meandiff
return sum(meandiffsq)
end

row, col = size(decisionMat)
normalizedDecisionMat = similar(decisionMat)
colminmax = zeros(Float64, col)
@inbounds for i in 1:col
colminmax[i] = decisionMat[:, i] |> fns[i]
if fns[i] == maximum
normalizedDecisionMat[:, i] = decisionMat[:, i] ./ colminmax[i]
elseif fns[i] == minimum
normalizedDecisionMat[:, i] = colminmax[i] ./ decisionMat[:, i]
end
end

pvs = zeros(Float64, row)
for i in 1:row
pvs[i] = PV(normalizedDecisionMat[i, :] |> collect)
end

phis = 1.0 .- pvs
sum_phis = sum(phis)

psis = phis ./ sum_phis

Is = zeros(Float64, row)
for i in 1:row
Is[i] = psis[i] .* collect(normalizedDecisionMat[i, :]) |> sum
end

scores = Is
ranks = sortperm(scores)
bestindex = ranks |> last

result = PSIResult(
scores,
ranks,
bestindex
)

return result
end


"""
psi(setting)
Apply PSI (Preference Selection Index) method for a given matrix and weights.
# Arguments:
- `setting::MCDMSetting`: MCDMSetting object.
# Description
psi() applies the PSI method to rank n alterntives subject to m criteria which are supposed to be
either maximized or minimized.
# Output
- `::PSIResult`: PSIResult object that holds multiple outputs including scores, rankings, and best index.
"""
function psi(setting::MCDMSetting)::PSIResult
psi(
setting.df,
setting.fns
)
end
9 changes: 9 additions & 0 deletions src/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ struct MCDMSetting
fns::Array{Function, 1}
end

struct PSIResult <: MCDMResult
scores::Array{Float64,1}
rankings::Array{Int, 1}
bestIndex::Int
end

struct TopsisResult <: MCDMResult
decisionMatrix::DataFrame
weights::Array{Float64,1}
Expand Down Expand Up @@ -377,6 +383,9 @@ end
struct MaircaMethod <: MCDMMethod
end

struct PSIMethod <: MCDMMethod
end


struct MooraMethod <: MCDMMethod
method::Symbol
Expand Down
18 changes: 18 additions & 0 deletions test.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using JMcDM
using DataFrames




df = DataFrame()
df[:, :x] = Float64[9, 8, 7]
df[:, :y] = Float64[7, 7, 8]
df[:, :z] = Float64[6, 9, 6]
df[:, :q] = Float64[7, 6, 6]
w = Float64[4, 2, 6, 8]

fns = makeminmax([maximum, maximum, maximum, maximum])

result = psi(df, fns)


23 changes: 23 additions & 0 deletions test/testmcdm.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
@testset "MCDM functions" begin

@testset "PSI" begin
tol = 0.0001

df = DataFrame()
df[:, :x] = Float64[9, 8, 7]
df[:, :y] = Float64[7, 7, 8]
df[:, :z] = Float64[6, 9, 6]
df[:, :q] = Float64[7, 6, 6]
w = Float64[4, 2, 6, 8]

fns = makeminmax([maximum, maximum, maximum, maximum])

result = psi(df, fns)

@test result isa PSIResult
@test result.bestIndex == 2
@test isapprox(
[1.1487059780663555, 1.252775986851622, 1.0884916686098811],
result.scores,
atol = tol
)
end

@testset "ROV" begin
tol = 0.01
mat = [
Expand Down

2 comments on commit 0414da9

@jbytecode
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator register()

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/60797

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.3.9 -m "<description of version>" 0414da96a52c5df793a5c47dcd4a3c746dd893bd
git push origin v0.3.9

Please sign in to comment.