European Extremely Large Telescope site selection

a comparison between real selection and multicriteria-decision-analysis suggestions

Juan B Cabral – Bruno O Sanchez – Manuel Starck Cuffini

Instituto de Astronomía Teórica y Experimental

jbcabral@oac.unc.edu.ar- bruno@oac.unc.edu.ar- mstarck@oac.unc.edu.ar

Data Mangling


In [1]:
import pandas as pd

import numpy as np

import skcriteria as sc
from skcriteria.madm import topsis, wsum, moora, wprod, electre

In [2]:
df = pd.read_csv("sites.csv")[:-3]
df


Out[2]:
Criteria/Alternatives Criteria Armazones Aklim ORM Ventarrones Macon
0 Final Status - Selected NaN Reference North Reference South NaN
1 Hemisphere - S N N S S
2 Location - Chile Morroco Canarias (Spain) Chile Argentina
3 Altitude (AMSL) max 3064 2350 2346 2837 4653
4 Total seeing ε (′′) min 0.64 1.00 0.80 0.91 0.87
5 Isoplanatic angle θ 0 (′′) max 2.04 1.29 1.93 1.96 1.37
6 Coherence time τ 0 (ms) max 4.6 3.53 5.58 4.90 3.37
7 Optical étendue G 0 (m 2 ms arcsec 2 ) min NaN 0.05 0.38 0.26 0.10
8 Free-atmosphere seeing (′′) ε FA max NaN 0.52 0.31 0.55 0.66
9 Boundary-layer seeing (′′) ε BL NaN NaN 0.77 0.65 0.60 0.51
10 Cloud: clear fraction (%) max 89 76 84 85 75
11 Night temperature at 2 m (°C) min 7.5 12.5 7.3 10.9 -0.2
12 Night relative humidity at 2 m (%) min 21 32 21 14 20
13 Night wind speed at 10 m (m=s) min 7.2* 6.2 8.2 5.9 11.3
14 Night pressure at 2 m min NaN 767.0 772.4 727.0 581.8

In [3]:
anames = df.columns[2:].values

def to_apply(r):
    new = []
    for e in r:
        if isinstance(e , str):
            e = float(e.replace("*", ""))
        new.append(e)
    return new

mtx = df[anames][3:].dropna()[anames].apply(to_apply).T.values
criteria = [sc.MIN if c == "min" else sc.MAX for c in df.Criteria[3:][~df.Armazones.isnull()].values]

cnames = df["Criteria/Alternatives"][3:].apply(lambda r: r.strip())[~df[3:].Armazones.isnull()].values
cnames = map(lambda s: s.decode("utf8"), cnames)

data = sc.Data(mtx, criteria, anames=anames, cnames=cnames)

Final Data


In [4]:
data


Out[4]:
ALT./CRIT. Altitude (AMSL) (max) Total seeing ε (′′) (min) Isoplanatic angle θ 0 (′′) (max) Coherence time τ 0 (ms) (max) Cloud: clear fraction (%) (max) Night temperature at 2 m (°C) (min) Night relative humidity at 2 m (%) (min) Night wind speed at 10 m (m=s) (min)
Armazones 3064 0.64 2.04 4.6 89 7.5 21 7.2
Aklim 2350 1 1.29 3.53 76 12.5 32 6.2
ORM 2346 0.8 1.93 5.58 84 7.3 21 8.2
Ventarrones 2837 0.91 1.96 4.9 85 10.9 14 5.9
Macon 4653 0.87 1.37 3.37 75 -0.2 20 11.3

In [5]:
dm = topsis.TOPSIS()
topsis_dec = dm.decide(data)
topsis_dec


Out[5]:

TOPSIS (mnorm=vector, wnorm=sum) - Solution:

ALT./CRIT. Altitude (AMSL) (max) Total seeing ε (′′) (min) Isoplanatic angle θ 0 (′′) (max) Coherence time τ 0 (ms) (max) Cloud: clear fraction (%) (max) Night temperature at 2 m (°C) (min) Night relative humidity at 2 m (%) (min) Night wind speed at 10 m (m=s) (min) Rank
Armazones 3064 0.64 2.04 4.6 89 7.5 21 7.2 2
Aklim 2350 1 1.29 3.53 76 12.5 32 6.2 5
ORM 2346 0.8 1.93 5.58 84 7.3 21 8.2 3
Ventarrones 2837 0.91 1.96 4.9 85 10.9 14 5.9 4
Macon 4653 0.87 1.37 3.37 75 -0.2 20 11.3 1

In [6]:
dm = wsum.MDWeightedSum()
wsum_dec = dm.decide(data)
wsum_dec


Out[6]:

MDWeightedSum (mnorm=sum, wnorm=sum) - Solution:

ALT./CRIT. Altitude (AMSL) (max) Total seeing ε (′′) (min) Isoplanatic angle θ 0 (′′) (max) Coherence time τ 0 (ms) (max) Cloud: clear fraction (%) (max) Night temperature at 2 m (°C) (min) Night relative humidity at 2 m (%) (min) Night wind speed at 10 m (m=s) (min) Rank
Armazones 3064 0.64 2.04 4.6 89 7.5 21 7.2 1
Aklim 2350 1 1.29 3.53 76 12.5 32 6.2 5
ORM 2346 0.8 1.93 5.58 84 7.3 21 8.2 4
Ventarrones 2837 0.91 1.96 4.9 85 10.9 14 5.9 3
Macon 4653 0.87 1.37 3.37 75 -0.2 20 11.3 2

In [7]:
dm = wprod.WeightedProduct()
wprod_dec = dm.decide(data)
wprod_dec


Out[7]:

WeightedProduct (mnorm=sum, wnorm=sum) - Solution:

ALT./CRIT. Altitude (AMSL) (max) Total seeing ε (′′) (min) Isoplanatic angle θ 0 (′′) (max) Coherence time τ 0 (ms) (max) Cloud: clear fraction (%) (max) Night temperature at 2 m (°C) (min) Night relative humidity at 2 m (%) (min) Night wind speed at 10 m (m=s) (min) Rank
Armazones 3064 0.64 2.04 4.6 89 7.5 21 7.2 2
Aklim 2350 1 1.29 3.53 76 12.5 32 6.2 5
ORM 2346 0.8 1.93 5.58 84 7.3 21 8.2 4
Ventarrones 2837 0.91 1.96 4.9 85 10.9 14 5.9 3
Macon 4653 0.87 1.37 3.37 75 -0.2 20 11.3 1

In [8]:
dm = electre.ELECTRE1()
electre_dec = dm.decide(data)
electre_dec


Out[8]:

ELECTRE1 (mnorm=sum, p=0.65, q=0.35, wnorm=sum) - Solution:

ALT./CRIT. Altitude (AMSL) (max) Total seeing ε (′′) (min) Isoplanatic angle θ 0 (′′) (max) Coherence time τ 0 (ms) (max) Cloud: clear fraction (%) (max) Night temperature at 2 m (°C) (min) Night relative humidity at 2 m (%) (min) Night wind speed at 10 m (m=s) (min) Kernel
Armazones 3064 0.64 2.04 4.6 89 7.5 21 7.2
Aklim 2350 1 1.29 3.53 76 12.5 32 6.2
ORM 2346 0.8 1.93 5.58 84 7.3 21 8.2
Ventarrones 2837 0.91 1.96 4.9 85 10.9 14 5.9
Macon 4653 0.87 1.37 3.37 75 -0.2 20 11.3 @

Results


In [9]:
methods = ["TOPSIS", "WSUM", "WPROD"]
kernel = np.array([1 if idx  in electre_dec.kernel_ else 0 for idx, _ in enumerate(anames)])
ranks = np.vstack((topsis_dec.rank_, wsum_dec.rank_, wprod_dec.rank_, kernel)).T
rdf = pd.DataFrame(ranks, index=anames, columns=methods + ["ELECTRE 1 Kernel"])
rdf


Out[9]:
TOPSIS WSUM WPROD ELECTRE 1 Kernel
Armazones 2 1 2 0
Aklim 5 5 5 0
ORM 3 4 4 0
Ventarrones 4 3 3 0
Macon 1 2 1 1

In [10]:
rdf[methods].T.describe()


Out[10]:
Armazones Aklim ORM Ventarrones Macon
count 3.000000 3.0 3.000000 3.000000 3.000000
mean 1.666667 5.0 3.666667 3.333333 1.333333
std 0.577350 0.0 0.577350 0.577350 0.577350
min 1.000000 5.0 3.000000 3.000000 1.000000
25% 1.500000 5.0 3.500000 3.000000 1.000000
50% 2.000000 5.0 4.000000 3.000000 1.000000
75% 2.000000 5.0 4.000000 3.500000 1.500000
max 2.000000 5.0 4.000000 4.000000 2.000000