# Notebook from lucasmiranda42/FaST-LMM
Path: doc/ipynb/FaST-LMM.ipynb

FaST-LMM Manual
=====
Factored Spectrally Transformed Linear Mixed Models
========

##### Version 0.5.0

FaST-LMM Team, February 20, 2021_____no_output_____### Introduction

FaST-LMM, which stands for Factored Spectrally Transformed Linear Mixed Models, is a program for performing
genome-wide association studies (GWAS) on datasets of all sizes, up to one millions samples.

See [FaST-LMM's README.md](https://github.com/fastlmm/FaST-LMM/blob/master/README.md) for installation instructions, documentation, code, and a bibliography.

### Contacts

* Email the developers at fastlmm-dev@python.org.
* [Join](mailto:fastlmm-user-join@python.org?subject=Subscribe) the user discussion and announcement list (or use [web sign up](https://mail.python.org/mailman3/lists/fastlmm-user.python.org)).
* [Open an issue](https://github.com/fastlmm/FaST-LMM/issues) on GitHub._____no_output_____### Citing FaST-LMM

If you use FaST-LMM in any published work, please cite [the relevant manuscript(s)](https://github.com/fastlmm/FaST-LMM/blob/master/README.md) describing it. _____no_output_____### Data preparation

This version of FaST-LMM is designed for use with randomly ascertained data with Gaussian residuals.  If you have case-control data with substantial ascertainment bias, you should first transform your phenotype(s) using [LEAP](https://github.com/omerwe/LEAP) (Weissbrod _et al._, _arXiv_ 2014).  If you are analyzing continuous phenotypes with non-Gaussian residuals, you should first transform your phenotype(s) using [Warped-LMM](https://github.com/PMBio/warpedLMM) (Fusi et al., _Nature Commun_ 2014).

FaST-LMM uses four input files containing (1) the SNP data to be tested, (2) the SNP data used to determine the genetic similarity matrix (GSM) between individuals (which can be different from 1), (3) the phenotype data, and (4, optionally) a set of covariates.

SNP files can be in PLINK format (ped/map, tped/tfam, bed/bim/fam, or fam/dat/map).  For the most speed, use the binary format in SNP major order.  See the PLINK manual http://pngu.mgh.harvard.edu/~purcell/plink/ (Purcell et al., _Am J Hum Genet_ 2007) for further details.  FaST-LMM also supports Hdf5 file format http://www.hdfgroup.org/HDF5/whatishdf5.html.  See https://github.com/fastlmm/PySnpTools for more details.  Note that each SNP will be standardized to have mean zero and standard deviation one across all individuals before processing.  Missing values are mean imputed.

The required file containing the phenotype uses the PLINK alternate phenotype format with no header.  The covariate file also uses this format (with additional columns for multiple covariates).
_____no_output_____### Notebook preparation and general use_____no_output_____To prepare this notebook to run analyses, please run the following script._____no_output_____

# set some ipython notebook properties
%matplotlib inline

# set degree of verbosity (adapt to INFO for more verbose output)
import logging
logging.basicConfig(level=logging.WARNING)

# set figure sizes
import pylab
pylab.rcParams['figure.figsize'] = (10.0, 8.0)
#pylab.plot([1,2,3],[4,5,6])

# set display width for pandas data frames
import pandas as pd
pd.set_option('display.width', 1000)_____no_output_____

If you would like to run any of the code below from the command line, first copy it into a file (_e.g._, `test.py`), and then run it by typing `python text.py` at the command line.

If you would like to see all the options for a function just type `? <function_name>` to an ipython prompt._____no_output_____### Single-SNP association testing

_____no_output_____#### Traditional analysis: LMM(all)_____no_output_____First, let's run a standard LMM analysis in which the GSM uses (almost) all available SNPs.  The model for this analysis is called LMM(all) in [Widmer et al., Scientific Reports 2014](http://www.nature.com/articles/srep06874).  We'll apply this model to the synthetic data in `tests\datasets\synth`.  The data has 500 samples with 5000 SNPs, and was generated from a Balding-Nichols model with FST=0.05.

When using a linear mixed model for association analysis, it is important to avoid proximal contamination ([Lippert _et al._, _Nat Meth_ 2011](http://www.nature.com/nmeth/journal/v8/n10/abs/nmeth.1681.html)). To understand proximal contamination, first note that a LMM with no fixed effects, using a realized relationship matrix for the GSM (as FaST-LMM does), is mathematically equivalent to linear regression of the SNPs on the phenotype, with weights integrated over independent Normal distributions having the same variance (_e.g._, Hayes _et al._, _Genet Res_ 2009).  That is, a LMM using a given set of SNPs for the GSM is equivalent to a form of linear regression using those SNPs as covariates to correct for confounding.  This equivalence implies that, when testing a given SNP, that SNP (and SNPs physically close to it) should be excluded from the computation of the GSM.  If not, when testing a particular SNP, we would also be using that same SNP as a covariate, making the log likelihood of the null model higher than it should be, thus leading to deflation of the test statistic and loss of power.

Excluding the SNP you are testing and those SNPs in close proximity to it from the GSM in a naïve way is extremely computationally expensive.  A computationally efficient approach for performing the exclusion is to use a GSM computed from all but chromosome $i$ when testing SNPs on chromosome $i$ ([Lippert _et al._, _Nat Meth_ 2011](http://www.nature.com/nmeth/journal/v8/n10/abs/nmeth.1681.html)).  We call this approach leave out one chromosome (LOOC).  The analysis here does this.
_____no_output_____

# import the algorithm
import numpy as np
from fastlmm.association import single_snp
from fastlmm.util import example_file # Download and return local file name

# set up data
##############################
from fastlmm.util import example_file # Download and return local file name
bed_fn = example_file('tests/datasets/synth/all.*','*.bed')
pheno_fn = example_file("tests/datasets/synth/pheno_10_causals.txt")
cov_fn = example_file("tests/datasets/synth/cov.txt")

# run gwas
###################################################################
results_df = single_snp(bed_fn, pheno_fn, covar=cov_fn, count_A1=False)

# manhattan plot
import pylab
import fastlmm.util.util as flutil
pylab.rcParams['figure.figsize'] = (10.0, 8.0)#For some reason, need again (appears above too) to get big figures
flutil.manhattan_plot(results_df[["Chr", "ChrPos", "PValue"]].values,pvalue_line=1e-5,xaxis_unit_bp=False)
pylab.show()

# qq plot
from fastlmm.util.stats import plotp
plotp.qqplot(results_df["PValue"].values, xlim=[0,5], ylim=[0,5])

# print head of results data frame
import pandas as pd
pd.set_option('display.width', 1000)
results_df.head(n=10)D:\OneDrive\programs\fastlmm\doc\ipynb

#### Improving power: LMM(all+select)_____no_output_____In [Widmer et al., Scientific Reports 2014](http://www.nature.com/articles/srep06874), we have shown that power can be increased while still maintaining control of type I error by using two GSMs: one based on all SNPs (`G0`) and one based on selected SNPs that are highly correlated with the phenotype (`G1`).  The model is called LMM(select + all).  This approach has greater computational demands (we recommend using a cluster computer when analyzing large data sets). Here is an example of how to apply this model to the synthetic data._____no_output_____

# example for two kernel feature selection
# this takes a couple of minutes to run on a 20-proc machine.
from pysnptools.snpreader import Bed
from fastlmm.association import single_snp_all_plus_select
from fastlmm.util import example_file # Download and return local file name

from pysnptools.util.mapreduce1.runner import LocalMultiProc
import multiprocessing
runner = LocalMultiProc(multiprocessing.cpu_count(),mkl_num_threads=4)

# define file names
bed_fn = example_file('tests/datasets/synth/all.*','*.bed')
snp_reader = Bed(bed_fn, count_A1=True)
pheno_fn = example_file("tests/datasets/synth/pheno_10_causals.txt")
cov_fn = example_file("tests/datasets/synth/cov.txt")

# find the chr5 SNPs
test_snps = snp_reader[:,snp_reader.pos[:,0] == 5]

#select the 2nd kernel and run GWAS
results_df = single_snp_all_plus_select(test_snps=test_snps,G=snp_reader,pheno=pheno_fn,GB_goal=2,do_plot=True,runner=runner)

import fastlmm.util.util as flutil
flutil.manhattan_plot(results_df[["Chr", "ChrPos", "PValue"]].values,pvalue_line=1e-5,xaxis_unit_bp=False)
pylab.show()

results_df.head()_____no_output_____

Aside: In some applications, you may want to use two kernels constructed from two pre-specified sets of SNPs (i.e., with no feature selection). Here we show how to do that and how to simultaneously find h2 and the mixing weight between the kernels._____no_output_____

# example script for two kernel without feature selection
import numpy as np
import pysnptools.util
from pysnptools.snpreader import Bed
from fastlmm.association import single_snp
from fastlmm.util import example_file # Download and return local file name

# define file names
bed_fn = example_file('tests/datasets/synth/all.*','*.bed')
snp_reader = Bed(bed_fn, count_A1=True)
pheno_fn = example_file("tests/datasets/synth/pheno_10_causals.txt")
cov_fn = example_file("tests/datasets/synth/cov.txt")

# select data
###################################################################
snp_reader = Bed(bed_fn,count_A1=True)

# partition snps on chr6 vs rest
G1_chr = 6
G0 = snp_reader[:,snp_reader.pos[:,0] != G1_chr]
G1 = snp_reader[:,snp_reader.pos[:,0] == G1_chr]

# test on chr5
test_snps = snp_reader[:,snp_reader.pos[:,0] == 5]

results_df = single_snp(test_snps, pheno_fn, K0=G0, K1=G1, covar=cov_fn, GB_goal=2, count_A1=True)

import fastlmm.util.util as flutil
flutil.manhattan_plot(results_df[["Chr", "ChrPos", "PValue"]].values,pvalue_line=1e-5,xaxis_unit_bp=False)
pylab.show()
results_df.head()_____no_output_____

#### Improving speed and memory use when there is little family strucutre: LMM(select)+PCs_____no_output_____In the same publication, we have shown that a simpler, more computationally efficient model can be used when the data is  confounded only by population structure and not by family structure or cryptic relatedness.  Under these circumstances, we have found that a model with a single GSM based on selected SNPs in combination with principle components as fixed-effect covariates yields good control of type I error and power.

This model, called LMM(select)+PCs, should be used with caution.  Even if you explicitly remove closely related individuals from your data, cryptic relatedness may remain.

To use this model, first identify the principle components to be used with the PCgeno algorithm as described in [Widmer et al., Scientific Reports 2014](http://www.nature.com/articles/srep06874).

Then you can call single_snp_select with these PCs as covariates._____no_output_____

from pysnptools.snpreader import Bed
from fastlmm.util import compute_auto_pcs
from fastlmm.association import single_snp_select
from fastlmm.util import example_file # Download and return local file name

# define file names
bed_fn = example_file('tests/datasets/synth/all.*','*.bed')
snp_reader = Bed(bed_fn, count_A1=True)
pheno_fn = example_file("tests/datasets/synth/pheno_10_causals.txt")
cov_fn = example_file("tests/datasets/synth/cov.txt")

# find number of PCs
pcs = compute_auto_pcs(bed_fn,count_A1=True)
print("selected number of PCs:", pcs["vals"].shape[1])

# test on chr5
test_snps = snp_reader[:,snp_reader.pos[:,0] == 5]

results_df = single_snp_select(test_snps=test_snps, G=snp_reader, pheno=pheno_fn, covar=pcs, GB_goal=2)

import fastlmm.util.util as flutil
flutil.manhattan_plot(results_df[["Chr", "ChrPos", "PValue"]].values,pvalue_line=1e-5,xaxis_unit_bp=False)
pylab.show()

results_df.head()selected number of PCs: 1

### Epistasis_____no_output_____You can test for epistatic interactions between pairs of SNPs as well.  Here is an example analysis applied to the same synthetic data.  Note that this version of the code uses a likelihood ratio test based on maximum-likelihood estimates.  A REML-based version is in the works._____no_output_____

# import the algorithm and reader
import numpy as np
from fastlmm.association import epistasis
from pysnptools.snpreader import Bed
from fastlmm.util import example_file # Download and return local file name

# define file names
bed_fn = example_file('tests/datasets/synth/all.*','*.bed')
bed_reader = Bed(bed_fn, count_A1=True)
pheno_fn = example_file("tests/datasets/synth/pheno_10_causals.txt")
cov_fn = example_file("tests/datasets/synth/cov.txt")

# partition data into the first 50 SNPs on chr1 and all but chr1
G0 = bed_reader[:,bed_reader.pos[:,0] != 1]
test_snps = bed_reader[:,bed_reader.pos[:,0] == 1][:,0:50]

# run epistasis analysis
results_df = epistasis(test_snps, pheno_fn, G0=G0, covar=cov_fn)

# qq plot
from fastlmm.util.stats import plotp
plotp.qqplot(results_df["PValue"].values, xlim=[0,5], ylim=[0,5])

# print head of results data frame
pd.set_option('display.width', 1000)
results_df.head(n=10)lambda=0.9234

### SNP-set association testing_____no_output_____SNP-set association testing is performed similarly to single-SNP testing, except we test sets of SNPs by putting them together into one GSM, separate from any (optional) background GSM that corrects for confounding.  Both LRT ("lrt") and score ("sc_davies") tests are supported.  The LRT is computed by default, but you can switch to the score test by setting test=“sc_davies”.  The score test can be more conservative in some settings (and hence can have less power), but it is closed form and often can be computed much faster.  The algorithms in their current form are described in [Lippert et al., Bioinformatics 2014](http://bioinformatics.oxfordjournals.org/content/30/22/3206).

Here is an example that uses the LRT and no background GSM--note the inflation in the results._____no_output_____

# this will take a few minutes to run

# import the algorithm and reader
import datetime
import numpy as np
from fastlmm.association import snp_set
from pysnptools.snpreader import Bed
from fastlmm.util import example_file # Download and return local file name

# define file names
test_snps_fn = example_file('tests/datasets/synth/chr1.*','*.bed').replace('.bed','')
pheno_fn = example_file("tests/datasets/synth/pheno_10_causals.txt")
cov_fn = example_file("tests/datasets/synth/cov.txt")
set_list_fn = example_file("tests/datasets/synth/chr1.sets.txt")
G0_fn = None

# run SNP-set analysis
results_df = snp_set(test_snps=test_snps_fn, G0=G0_fn, set_list=set_list_fn, pheno=pheno_fn, covar=cov_fn, test="lrt")

# qq plot
from fastlmm.util.stats import plotp

plotp.qqplot(results_df["P-value"].values, xlim=[0,5], ylim=[0,5], addlambda=False, legend="QQ")

# print head of results data frame
import pandas as pd
pd.set_option('display.width', 1000)
results_df.head(n=10)_____no_output_____

As just mentioned, we see inflation of the test statistic because no background GSM was used. When you include a background GSM (using `G0`) type I error is better controlled. As in the single SNP case, proximal contamination is avoided using LOOC._____no_output_____

#Here we give a G0, the GSM, created via Bed file with all the chroms except chrom 1 (the test snps)

#WARNING: Even on a fast machine, this takes 24 minutes (but, running with test="sc_davies" takes seconds)

bed_reader = Bed(example_file('tests/datasets/synth/all.*','*.bed'),count_A1=True)
chrNot1 = bed_reader[:,bed_reader.pos[:,0] != 1]
G0_fn = 'tempChrNot1.bed'
Bed.write(G0_fn,chrNot1.read(),count_A1=True)

# run SNP-set analysis
results_df = snp_set(test_snps=test_snps_fn, G0=G0_fn, set_list=set_list_fn, pheno=pheno_fn, covar=cov_fn, test="lrt")

# qq plot
plotp.qqplot(results_df["P-value"].values, xlim=[0,5], ylim=[0,5], addlambda=False, legend="QQ")

# print head of results data frame
import pandas as pd
pd.set_option('display.width', 1000)
results_df.head(n=10)constructing LMM - this should only happen once.

The `G1` option available for single-marker testing is not yet supported for set testing. With the same caveats as previously described, however, you can use PC covariates and a `G0` based on selected SNPs when your data is confounded, for example, by population structure._____no_output_____### Prediction

We can train on one set of examples and make predictions on another set._____no_output_____

from pysnptools.snpreader import Pheno, Bed
from fastlmm.inference import FastLMM
import numpy as np
from fastlmm.util import example_file # Download and return local file name

# define file names
bed_fn = example_file('tests/datasets/synth/all.*','*.bed')
snp_reader = Bed(bed_fn, count_A1=True)
pheno_fn = example_file("tests/datasets/synth/pheno_10_causals.txt")
cov_fn = example_file("tests/datasets/synth/cov.txt")

# Divide the data into train (all but the last 10 individuals) and test (the last 10 individuals)
# (the cov and pheno will automatially be divided to match)
train = snp_reader[:-10,:]
test = snp_reader[-10:,:]

# In the style of scikit-learn, create a predictor and then train it.
fastlmm = FastLMM(GB_goal=2)
fastlmm.fit(K0_train=train,X=cov_fn,y=pheno_fn)

# Now predict with it
mean, covariance = fastlmm.predict(K0_whole_test=test,X=cov_fn)

print("Predicted means and stdevs")
print(mean.val[:,0])
print(np.sqrt(np.diag(covariance.val)))

#Plot actual phenotype and predicted phenotype
whole_pheno = Pheno(pheno_fn)
actual_pheno = whole_pheno[whole_pheno.iid_to_index(mean.iid),:].read()
pylab.plot(actual_pheno.val,"r.")
pylab.plot(mean.val,"b.")
pylab.errorbar(np.arange(mean.iid_count),mean.val,yerr=np.sqrt(np.diag(covariance.val)),fmt='.')
pylab.xlabel('testing examples')
pylab.ylabel('phenotype, actual (red) and predicted (blue with stdev)')
pylab.show()Predicted means and stdevs
[-0.01441867 -0.28643403 -0.25428547  0.08895203 -0.2951101  -0.39238038
  0.06844377 -0.43282082 -0.21734717 -0.35522391]
[0.95795614 0.96181176 0.95260425 0.9499153  0.9604778  0.95411447
 0.96020393 0.9817148  0.95198988 0.96271045]

### Appendix_____no_output_____Columns in the output are as follows:

`SNP` or `Set`
The SNP or set identifier tested.

`PValue`
The _P_ value computed for the SNP tested.

`NullLogLike`
The log likelihood of the null model.

`AltLogLike`
The log likelihood of the alternative model.

Global statistics, such as sample size and the number of SNPs tested are printed to stdout at the end of the run.

###### Single-SNP testing only:

`Chr`
The chromosome identifier for the SNP tested or 0 if unplaced.  Taken from the PLINK file.

`GenDist`
The genetic distance of the SNP on the chromosome.  Taken from the PLINK file.  Any units are allowed, but typically centimorgans or morgans are used.

`ChrPos`
The base-pair position of the SNP on the chromosome (bp units).  Taken from the PLINK file.

`SnpWeight`
The fixed-effect weight of the SNP.

`SnpWeightSE`
The standard error of the SnpWeight.

`Nullh2`
The narrow sense heritability given by h2 $=σ_g^2/(σ_g^2+σ_e^2)$ on the null model.

###### SNP-set testing only:

`#SNPs_in_Set`
The number of markers in the set.

`Alt_h2`
The value found in the alt. model, given by $\sigma^2(h2(a2*K_S+(1-a2) * K_C)+(1-h2)*I)$, where $K_C$ is the NxN matrix to correct for confounding, and $K_S$ is the matrix containing SNPs to be tested.

`Alt_a2`
See Alt_h2.

`Null_h2`
The value found in the null model, given by $\sigma^2(h2*K_C+1-h2)*I)$, where $K_C$ is the $N \times N$ matrix to correct for confounding.
_____no_output_____


# Notebook from enayatmoallemi/Moallemi_et_al_SDG_SSP_Assessment
Path: Notebook/.ipynb_checkpoints/Morris_ranking_convergence-checkpoint.ipynb

import sys
sys.path.append(r'C:\Users\moallemie\EMAworkbench-master')
sys.path.append(r'C:\Users\moallemie\GitHub\SSPs_SDGs_Assessment')_____no_output_____import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from ema_workbench import load_results, ema_logging
from ema_workbench.em_framework.salib_samplers import get_SALib_problem
from SALib.analyze import morris_____no_output_____

## Loading SA results_____no_output_____

# Replace the original name of outputs with a shortened version for plotting
ylabels = {'Total Population Indicator':'Total Population' ,
             'Total Primary Education Graduates Indicator':'Primary Education Graduates',
             'Total Secondary Education Graduates Indicator':'Secondary Education Graduates',
              'Total Tertiary Education Graduates Indicator':'Tertiary Education Graduates',
              'GWP per Capita Indicator':'GWP per Capita',
             'Energy Demand Indicator': 'Energy Demand',
             'Coal Production Indicator':'Coal Production',
              'Gas Production Indicator':'Gas Production',
              'Oil Production Indicator':'Oil Production',
              'Solar Energy Production Indicator':'Solar Energy Production',
              'Wind Energy Production Indicator':'Wind Energy Production',
            'Biomass Energy Production Indicator':'Biomass Energy Production',
             'Nonenergy Crops Production Indicator':'Nonenergy Crops Production',
              'Livestock Production Indicator':'Livestock Production',
              'Total Croplands Indicator':'Total Croplands',
             'Forest Land Indicator':'Forest Land',
              'Pasture Land Indicator':'Pasture Land',
           'Urban Industrial Land Indicator' :  'Urban Industrial Land',
              'CO2 Radiative Forcing Indicator': 'CO2 Radiative Forcing',
             'Total CO2 Emissions Indicator': 'Total CO2 Emissions'}
_____no_output_____# Count how many lines we will have in the top 10 parameters across outcomes for assigning linestyles and colors in legends
new_mu_df = pd.DataFrame()
t = 2100
sc = 5000 # Specify the highest number of scenarios you ran
outcome_vars = list(ylabels.keys())
sa_dir = 'C:/Users/moallemie/GitHub/SSPs_SDGs_Assessment/Results/Morris_results/'
for i, outcome_var in enumerate(outcome_vars):
    mu_df = pd.read_csv(sa_dir+"MorrisIndices_{}_sc{}_t{}_test.csv".format(outcome_var, sc, t))
    mu_df.set_index('Unnamed: 0', inplace=True)
    mu_df.index.names = ['Uncertainty']
    mu_df.rename(columns={'mu_star': outcome_var}, inplace=True)
    mu_df.drop(['mu_star_conf', 'mu', 'sigma'], axis=1, inplace=True)
    mu_df.sort_values(by=[outcome_var], ascending=False, inplace=True)
    mu_df2 = mu_df.head(10)
    new_mu_df = pd.concat([new_mu_df, mu_df2], axis=1)_____no_output_____# Create a dictionary for linestyle and linecolor for the figure legend.
color_list = sns.color_palette()

df_indices = [item[:-10] for item in new_mu_df.index.tolist()]
line_keys = {}
for p, par in enumerate(df_indices):
    if p < 10:
        line_keys[par] = ['-', color_list[p]]
    if p>=10 and p<20:
        line_keys[par] = ['--', color_list[p-10]]
    if p>=20 and p<30:
        line_keys[par] = ['-.', color_list[p-20]]
    if p>=30 and p<40:
        line_keys[par] = [':', color_list[p-30]]
    if p>=40 and p<50:
        line_keys[par] = [(0, (3, 5, 1, 5, 1, 5)), color_list[p-40]]
    _____no_output_____from textwrap import wrap
from matplotlib.lines import Line2D

outcome_vars = list(ylabels.keys())

sa_dir='C:/Users/moallemie/GitHub/SSPs_SDGs_Assessment/Results/Morris_results/'

color_list = sns.color_palette()

fig, axes = plt.subplots(5, 4, figsize=(18, 15), sharex='col')
fig.subplots_adjust(hspace=0.15, wspace=0.15)

for v, outcome_var in enumerate(outcome_vars):
    df = pd.DataFrame(columns=[250, 500, 750, 1000, 1500, 2000, 3000, 5000])
    t = 2100
    dic = {}
    for sc in [250, 500, 750, 1000, 1500, 2000, 3000, 5000]:
        dic[sc] = pd.read_csv(sa_dir+"MorrisIndices_{}_sc{}_t{}_test.csv".format(outcome_var, sc, t), index_col=0)
        df[sc] = dic[sc]['mu_star']

    df.sort_values(by=[5000], ascending=False, inplace=True)
    df = df.head(10)

    # To remove Variation from the end of uncertain parameters in the final plots
    idx = [item for item in df.index]
    new_idx = [item[:-10] for item in df.index]
    df = df.rename(index=dict(zip(idx,new_idx)))

    if v < 5:
        x = v
        y = 0
    elif v>=5 and v<10:
        x = v-5
        y = 1
    elif v>=10 and v<15:
        x = v-10
        y = 2
    elif v>=15 and v<20:
        x = v-15
        y = 3

    for i, par in enumerate(df.index):
        if i < 10:
            df.iloc[i, :].plot(ax=axes[x,y], linestyle=line_keys[par][0], marker='o', color=line_keys[par][1], linewidth=1, legend=False, fontsize=8)
        elif i>=10 and i<20:
            df.iloc[i, :].plot(ax=axes[x,y], linestyle=line_keys[par][0], color=line_keys[par][1], linewidth=1, legend=False, fontsize=8)
        elif i>=20 and i<30:
            df.iloc[i, :].plot(ax=axes[x,y], linestyle=line_keys[par][0], color=line_keys[par][1], linewidth=1, legend=False, fontsize=8)
        elif i>=30 :
            df.iloc[i, :].plot(ax=axes[x,y], linestyle=line_keys[par][0], color=line_keys[par][1], linewidth=1, legend=False, fontsize=8)

    axes[x,y].set_xticks([250, 500, 750, 1000, 1500, 2000, 3000, 5000])
    # Calculate the number of Morris experiments scenarios * (parameters+1)
    axes[x,y].set_xticklabels([250*113, 500*113, 750*112, 1000*113, 1500*113, 2000*113, 3000*113, 5000*113],
                              rotation=90, fontsize=9, ha='center')
    axes[x,y].set_xlabel("Number of experiments (N)", fontsize=10)
    axes[x,0].set_ylabel("Morris mu star", fontsize=10)
    axes[x,y].annotate('\n'.join(wrap(ylabels[outcome_var], 30)), xy=(.5, 1.01), fontsize=10, xycoords='axes fraction', ha='center')
    #ax[x,y].legend().set_visible(False)

fig.subplots_adjust(bottom=0.2) # <-- Change the 0.2 to work for your plot.

legend_elements = []
for p, par in enumerate(df_indices):
    legend_elements = legend_elements + [Line2D([0], [0], linestyle=line_keys[par][0], color=line_keys[par][1], marker="o", lw=2, label=par)]
fig.legend(handles=legend_elements, loc='lower center', ncol=4, fontsize=8.5, bbox_to_anchor=(0.45, .01), fancybox=True, borderaxespad=0)

fig_dir='C:/Users/moallemie/GitHub/SSPs_SDGs_Assessment/Fig/'
plt.savefig(fig_dir+"Morris_covergence_SI.pdf", dpi=600, bbox_inches='tight')
plt.savefig(fig_dir+"Morris_covergence_SI.png", dpi=600, bbox_inches='tight')    _____no_output_____


# Notebook from PyTorchKorea/lightning-kr
Path: source/notebooks/lightning_examples/mnist-hello-world.ipynb

# Introduction to Pytorch Lightning

* **Author:** PL team
* **License:** CC BY-SA
* **Generated:** 2021-12-04T16:53:03.416116

In this notebook, we'll go over the basics of lightning by preparing models to train on the [MNIST Handwritten Digits dataset](https://en.wikipedia.org/wiki/MNIST_database).

---
Open in [![Open In Colab](data:image/png;base64,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){height="20px" width="117px"}](https://colab.research.google.com/github/PytorchLightning/lightning-tutorials/blob/publication/.notebooks/lightning_examples/mnist-hello-world.ipynb)

Give us a ⭐ [on Github](https://www.github.com/PytorchLightning/pytorch-lightning/)
| Check out [the documentation](https://pytorch-lightning.readthedocs.io/en/latest/)
| Join us [on Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)_____no_output_____## Setup
This notebook requires some packages besides pytorch-lightning._____no_output_____

! pip install --quiet "torchvision" "torch>=1.6, <1.9" "pytorch-lightning>=1.3" "torchmetrics>=0.3"_____no_output_____import os

import torch
from pytorch_lightning import LightningModule, Trainer
from torch import nn
from torch.nn import functional as F
from torch.utils.data import DataLoader, random_split
from torchmetrics import Accuracy
from torchvision import transforms
from torchvision.datasets import MNIST

PATH_DATASETS = os.environ.get("PATH_DATASETS", ".")
AVAIL_GPUS = min(1, torch.cuda.device_count())
BATCH_SIZE = 256 if AVAIL_GPUS else 64_____no_output_____

## Simplest example

Here's the simplest most minimal example with just a training loop (no validation, no testing).

**Keep in Mind** - A `LightningModule` *is* a PyTorch `nn.Module` - it just has a few more helpful features._____no_output_____

class MNISTModel(LightningModule):
    def __init__(self):
        super().__init__()
        self.l1 = torch.nn.Linear(28 * 28, 10)

    def forward(self, x):
        return torch.relu(self.l1(x.view(x.size(0), -1)))

    def training_step(self, batch, batch_nb):
        x, y = batch
        loss = F.cross_entropy(self(x), y)
        return loss

    def configure_optimizers(self):
        return torch.optim.Adam(self.parameters(), lr=0.02)_____no_output_____

By using the `Trainer` you automatically get:
1. Tensorboard logging
2. Model checkpointing
3. Training and validation loop
4. early-stopping_____no_output_____

# Init our model
mnist_model = MNISTModel()

# Init DataLoader from MNIST Dataset
train_ds = MNIST(PATH_DATASETS, train=True, download=True, transform=transforms.ToTensor())
train_loader = DataLoader(train_ds, batch_size=BATCH_SIZE)

# Initialize a trainer
trainer = Trainer(
    gpus=AVAIL_GPUS,
    max_epochs=3,
    progress_bar_refresh_rate=20,
)

# Train the model ⚡
trainer.fit(mnist_model, train_loader)/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/connectors/callback_connector.py:90: LightningDeprecationWarning: Setting `Trainer(progress_bar_refresh_rate=20)` is deprecated in v1.5 and will be removed in v1.7. Please pass `pytorch_lightning.callbacks.progress.TQDMProgressBar` with `refresh_rate` directly to the Trainer's `callbacks` argument instead. Or, to disable the progress bar pass `enable_progress_bar = False` to the Trainer.
  rank_zero_deprecation(
GPU available: True, used: True

## A more complete MNIST Lightning Module Example

That wasn't so hard was it?

Now that we've got our feet wet, let's dive in a bit deeper and write a more complete `LightningModule` for MNIST...

This time, we'll bake in all the dataset specific pieces directly in the `LightningModule`.
This way, we can avoid writing extra code at the beginning of our script every time we want to run it.

---

### Note what the following built-in functions are doing:

1. [prepare_data()](https://pytorch-lightning.readthedocs.io/en/stable/common/lightning_module.html#prepare-data) 💾
    - This is where we can download the dataset. We point to our desired dataset and ask torchvision's `MNIST` dataset class to download if the dataset isn't found there.
    - **Note we do not make any state assignments in this function** (i.e. `self.something = ...`)

2. [setup(stage)](https://pytorch-lightning.readthedocs.io/en/stable/common/lightning_module.html#setup) ⚙
    - Loads in data from file and prepares PyTorch tensor datasets for each split (train, val, test).
    - Setup expects a 'stage' arg which is used to separate logic for 'fit' and 'test'.
    - If you don't mind loading all your datasets at once, you can set up a condition to allow for both 'fit' related setup and 'test' related setup to run whenever `None` is passed to `stage` (or ignore it altogether and exclude any conditionals).
    - **Note this runs across all GPUs and it *is* safe to make state assignments here**

3. [x_dataloader()](https://pytorch-lightning.readthedocs.io/en/stable/api/pytorch_lightning.core.hooks.html) ♻
    - `train_dataloader()`, `val_dataloader()`, and `test_dataloader()` all return PyTorch `DataLoader` instances that are created by wrapping their respective datasets that we prepared in `setup()`_____no_output_____

class LitMNIST(LightningModule):
    def __init__(self, data_dir=PATH_DATASETS, hidden_size=64, learning_rate=2e-4):

        super().__init__()

        # Set our init args as class attributes
        self.data_dir = data_dir
        self.hidden_size = hidden_size
        self.learning_rate = learning_rate

        # Hardcode some dataset specific attributes
        self.num_classes = 10
        self.dims = (1, 28, 28)
        channels, width, height = self.dims
        self.transform = transforms.Compose(
            [
                transforms.ToTensor(),
                transforms.Normalize((0.1307,), (0.3081,)),
            ]
        )

        # Define PyTorch model
        self.model = nn.Sequential(
            nn.Flatten(),
            nn.Linear(channels * width * height, hidden_size),
            nn.ReLU(),
            nn.Dropout(0.1),
            nn.Linear(hidden_size, hidden_size),
            nn.ReLU(),
            nn.Dropout(0.1),
            nn.Linear(hidden_size, self.num_classes),
        )

        self.accuracy = Accuracy()

    def forward(self, x):
        x = self.model(x)
        return F.log_softmax(x, dim=1)

    def training_step(self, batch, batch_idx):
        x, y = batch
        logits = self(x)
        loss = F.nll_loss(logits, y)
        return loss

    def validation_step(self, batch, batch_idx):
        x, y = batch
        logits = self(x)
        loss = F.nll_loss(logits, y)
        preds = torch.argmax(logits, dim=1)
        self.accuracy(preds, y)

        # Calling self.log will surface up scalars for you in TensorBoard
        self.log("val_loss", loss, prog_bar=True)
        self.log("val_acc", self.accuracy, prog_bar=True)
        return loss

    def test_step(self, batch, batch_idx):
        # Here we just reuse the validation_step for testing
        return self.validation_step(batch, batch_idx)

    def configure_optimizers(self):
        optimizer = torch.optim.Adam(self.parameters(), lr=self.learning_rate)
        return optimizer

    ####################
    # DATA RELATED HOOKS
    ####################

    def prepare_data(self):
        # download
        MNIST(self.data_dir, train=True, download=True)
        MNIST(self.data_dir, train=False, download=True)

    def setup(self, stage=None):

        # Assign train/val datasets for use in dataloaders
        if stage == "fit" or stage is None:
            mnist_full = MNIST(self.data_dir, train=True, transform=self.transform)
            self.mnist_train, self.mnist_val = random_split(mnist_full, [55000, 5000])

        # Assign test dataset for use in dataloader(s)
        if stage == "test" or stage is None:
            self.mnist_test = MNIST(self.data_dir, train=False, transform=self.transform)

    def train_dataloader(self):
        return DataLoader(self.mnist_train, batch_size=BATCH_SIZE)

    def val_dataloader(self):
        return DataLoader(self.mnist_val, batch_size=BATCH_SIZE)

    def test_dataloader(self):
        return DataLoader(self.mnist_test, batch_size=BATCH_SIZE)_____no_output_____model = LitMNIST()
trainer = Trainer(
    gpus=AVAIL_GPUS,
    max_epochs=3,
    progress_bar_refresh_rate=20,
)
trainer.fit(model)GPU available: True, used: True

### Testing

To test a model, call `trainer.test(model)`.

Or, if you've just trained a model, you can just call `trainer.test()` and Lightning will automatically
test using the best saved checkpoint (conditioned on val_loss)._____no_output_____

trainer.test()/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/trainer.py:1393: UserWarning: `.test(ckpt_path=None)` was called without a model. The best model of the previous `fit` call will be used. You can pass `test(ckpt_path='best')` to use and best model checkpoint and avoid this warning or `ckpt_path=trainer.model_checkpoint.last_model_path` to use the last model.
  rank_zero_warn(
Restoring states from the checkpoint path at /__w/1/s/lightning_logs/version_7/checkpoints/epoch=2-step=644.ckpt

### Bonus Tip

You can keep calling `trainer.fit(model)` as many times as you'd like to continue training_____no_output_____

trainer.fit(model)LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]

In Colab, you can use the TensorBoard magic function to view the logs that Lightning has created for you!_____no_output_____

# Start tensorboard.
%load_ext tensorboard
%tensorboard --logdir lightning_logs/_____no_output_____

## Congratulations - Time to Join the Community!

Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the Lightning
movement, you can do so in the following ways!

### Star [Lightning](https://github.com/PyTorchLightning/pytorch-lightning) on GitHub
The easiest way to help our community is just by starring the GitHub repos! This helps raise awareness of the cool
tools we're building.

### Join our [Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)!
The best way to keep up to date on the latest advancements is to join our community! Make sure to introduce yourself
and share your interests in `#general` channel

### Contributions !
The best way to contribute to our community is to become a code contributor! At any time you can go to
[Lightning](https://github.com/PyTorchLightning/pytorch-lightning) or [Bolt](https://github.com/PyTorchLightning/lightning-bolts)
GitHub Issues page and filter for "good first issue".

* [Lightning good first issue](https://github.com/PyTorchLightning/pytorch-lightning/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)
* [Bolt good first issue](https://github.com/PyTorchLightning/lightning-bolts/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)
* You can also contribute your own notebooks with useful examples !

### Great thanks from the entire Pytorch Lightning Team for your interest !

[![Pytorch Lightning](data:image/png;base64,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){height="60px" width="240px"}](https://pytorchlightning.ai)_____no_output_____


# Notebook from librairy/extractiveQA-over-KG
Path: notebooks/backup/muheqa-evaluation (1).ipynb

# Wikidata Simple-Question with Answers

We consider only questions with a single answer (predicate = 'P')._____no_output_____

import pandas as pd
df = pd.read_csv('wikidata-sqa2.csv', index_col=0)
df.head()_____no_output_____

### Named Entity Recognition based on Language Models, PoS tagging and Subwords_____no_output_____

#!pip install --upgrade --user pip
#!pip install --user flair
from flair.data import Sentence
from flair.models import SequenceTagger

# load tagger
tagger = SequenceTagger.load("flair/pos-english")
2022-04-12 11:50:33,823 loading file /home/jupyter-cbadenes/.flair/models/pos-english/a9a73f6cd878edce8a0fa518db76f441f1cc49c2525b2b4557af278ec2f0659e.121306ea62993d04cd1978398b68396931a39eb47754c8a06a87f325ea70ac63
2022-04-12 11:50:34,397 SequenceTagger predicts: Dictionary with 53 tags: <unk>, O, UH, ,, VBD, PRP, VB, PRP$, NN, RB, ., DT, JJ, VBP, VBG, IN, CD, NNS, NNP, WRB, VBZ, WDT, CC, TO, MD, VBN, WP, :, RP, EX, JJR, FW, XX, HYPH, POS, RBR, JJS, PDT, NNPS, RBS, AFX, WP$, -LRB-, -RRB-, ``, '', LS, $, SYM, ADD
def get_pos_entities(text,category):
    # make example sentence
    sentence = Sentence(text)

    # predict NER tags
    tagger.predict(sentence)

    # print sentence
    #print(sentence)
    # iterate over entities and print
    entities = []
    current_entity = ""
    for t in sentence.tokens:
        for label in t.annotation_layers.keys():
            text = t.text
            label = t.get_labels(label)[0].value
            if (label == category):
                if (current_entity == ""):
                    current_entity += text
                else:
                    current_entity += " " + text
            elif len(current_entity) > 0:
                entities.append(current_entity)
                current_entity = ""

    if (len(current_entity)>0):
        entities.append(current_entity)
    return entities

r = get_pos_entities("who's a kung fu star from hong kong","NN")
print(r)['kung fu star']
from transformers import AutoTokenizer, AutoModelForTokenClassification
from transformers import pipeline

tokenizer = AutoTokenizer.from_pretrained("dslim/bert-base-NER-uncased")
model = AutoModelForTokenClassification.from_pretrained("dslim/bert-base-NER-uncased")

nlp = pipeline("ner", model=model, tokenizer=tokenizer)_____no_output_____def get_entities(text):
    entities = []
    entity = ""
    index = -1
    offset = -1
    for token in nlp(text):
        if (index == -1):
            index = token['index']
            offset = token['start']
        word = token['word']
        if (word[0] == '#'):
            word = token['word'].replace("#","")

        if (token['start']== offset):
            entity += word
        elif (token['index']-index < 2):
            entity += " " + word
        else:
            entities.append(entity)
            entity = word
        index = token['index']
        offset = token['end']

    if (len(entity) > 0):
        entities.append(entity)
    if (len(entities) == 0):
        cardinal_entities =  get_pos_entities(text,"CD")
        if (len(cardinal_entities)>0):
            return cardinal_entities
        noun_entities =  get_pos_entities(text,"NN")
        if (len(noun_entities)>0):
            return noun_entities

    return entities

r = get_entities("which city did carl-alfred schumacher die")
print(r)['carl-alfred schumacher']

## Identification of entities_____no_output_____

entities = []
for index,row in df.iterrows():
    question = row['question']
    print(index,":",question)
    q_entities = get_entities(question)
    print("\t entities:",q_entities)
    if (len(q_entities)<1):
        print("No entities found!")
        entities.append("")
    elif (len(q_entities)>1):
        print("More than one entity found!")
        entities.append(q_entities)
    else:
        entities.append(q_entities[0])0 : Where did roger marquis die
	 entities: ['roger marquis']
1 : what was the cause of death of yves klein
	 entities: ['yves klein']
2 : What position does carlos gomez play?
	 entities: ['carlos gomez']
3 : how does engelbert zaschka identify
	 entities: ['engelbert zaschka']
4 : what position does pee wee reese play in baseball
	 entities: ['pee wee reese']
5 : Which Swiss conductor's cause of death is myocardial infarction?
	 entities: ['swiss']
6 : where was padraic mcguinness's place of death
	 entities: ['padraic mcguinness']
7 : what is the place of birth of sam edwards?
	 entities: ['sam edwards']
8 : Which home is an example of italianate architecture?
	 entities: ['italianate']
9 : who published neo contra
	 entities: ['neo contra']
10 : what is angie estes's profession
	 entities: ['angie estes']
11 : what position does josé francisco torres play?
	 entities: ['jose francisco torres']
12 : what male actor was born in  warsaw
	 entities: ['warsaw']
13 : who was also born in jakarta
	 entities: ['jakarta']
14 : Who was born in prague
	 entities: ['prague']
15 : where was guy pnini born
	 entities: ['guy pnini']
16 : what is an album recorded by scott grimes
	 entities: ['scott grimes']
17 : what was the country of origin of the tv show sidewalks entertainment
	 entities: ['sidewalks entertainment']
18 : who was the architect of the structure tour perret
	 entities: ['tour perret']
19 : What is an album by guy clark?
	 entities: ['guy clark']
20 : what's the name of an Australian rock and roll
	 entities: ['australian', 'roll']
More than one entity found!
21 : what is the category of the celestial object 1241 dysona
	 entities: ['dysona']
22 : What is a type of gameplay available to gamers playing custom robo v2
	 entities: ['robo v']
23 : What's an example of a romance film
	 entities: ['example', 'romance film']
More than one entity found!
24 : where was shigeyasu suzuki's place of birth
	 entities: ['shigeyasu suzuki']
25 : which nationality is yonatan rozen
	 entities: ['yonatan rozen']
26 : which indian city was fazil born in
	 entities: ['indian', 'fazil']
More than one entity found!
27 : where was paolo de la haza born?
	 entities: ['paolo de la haza']
28 : what gender is daniela cristofori
	 entities: ['daniela cristofori']
29 : in which country was overnight delivery filmed in
	 entities: ['country', 'delivery']
More than one entity found!
30 : what is the second level division of the division crixás do tocantins
	 entities: ['crixas do tocantins']
31 : Which film was directed by ian iqbal rashid
	 entities: ['ian iqbal rashid']
32 : in what county is monango found
	 entities: ['monango']
33 : who recorded rca records
	 entities: ['who', 'rca records']
More than one entity found!
34 : who is the artist of breathing your love?
	 entities: ['artist', 'love']
More than one entity found!
35 : What is a gampelay mode found in the game allegiance
	 entities: ['gampelay', 'allegiance']
More than one entity found!
36 : What type of film is raghuvinte swantham raziya
	 entities: ['raghuvinte swantham raziya']
37 : Which films has leonid gaidai written
	 entities: ['which films', 'leonid gaidai']
More than one entity found!
38 : What is an organization that j. a. folger founded?
	 entities: ['j. a. folger']
39 : What is shinji mori's gender?
	 entities: ['shinji mori']
40 : what style of msuic did john pizzarelli play
	 entities: ['msui', 'john pizzarelli']
More than one entity found!
41 : what does  2674 pandarus orbit
	 entities: ['pandarus']
42 : what is trond egil soltvedt's birth place
	 entities: ['trond egil soltvedt']
43 : what series is super mario bros. 2 a part of
	 entities: ['super mario bros. 2']
44 : what disease did anthony perkins die of
	 entities: ['anthony perkins']
45 : what is the mouth of the schuylkill river
	 entities: ['schuylkill']
46 : Which genre of book is bin laden: the man who declared war on america?
df['entity']=entities
df.to_csv('wikidata-sqa-e.csv')
df.head()_____no_output_____

### Wikidata Entity Linking based on MediaWiki API

The MediaWiki Action API is a web service that allows access to some wiki-features like authentication, page operations, and search. It can provide meta information about the wiki and the logged-in user.

action=wbsearchentities

Searches for entities using labels and aliases.

Returns a label and description for the entity in the user language if possible. Returns details of the matched term. The matched term text is also present in the aliases key if different from the display label._____no_output_____

import requests
def get_wikidata_candidates(label):
    candidates = []
    if (label==""):
        return candidates
    # type: One of the following values: form, form, item, lexeme, property, sense, sense
    query_path = "https://www.wikidata.org/w/api.php?action=wbsearchentities&search=QUERY_TEXT&language=en&limit=10&type=item&format=json"
    r = requests.get(query_path.replace("QUERY_TEXT",label))

    for answer in r.json()['search']:
        candidate = {
            'label': answer['display']['label']['value'],
            'id':answer['id']
#            'description' : answer['display']['description']
        }
        candidates.append(candidate)
    return candidates_____no_output_____entities = []
wikidata_items = []
for index,row in df.iterrows():
    question = row['question']
    print(index,":",question)
    q_entities = get_entities(question)
    print("\t entities:",q_entities)
    if (len(q_entities)<1):
        print("No entities found!")
        entities.append("")
        wikidata_item.append("")
    elif (len(q_entities)>1):
        print("More than one entity found!")
        entities.append(q_entities)
    else:
        entities.append(q_entities[0])
    q_wiki_entities = []
    for entity in q_entities:
        for item in get_wikidata_candidates(entity):
            q_wiki_entities.append(item['id'])
    print("\t wiki:",q_wiki_entities)
    wikidata_items.append(q_wiki_entities)
    print("\t reference:",row['subject'])
_____no_output_____df['wikidata']=wikidata_items
df.to_csv('wikidata-sqa-ew.csv')
df.head()_____no_output_____from sklearn.metrics import confusion_matrix
y_true = df['subject']
y_pred = df['wikidata']
confusion_matrix(y_true, y_pred, labels=df['entity'])
_____no_output_____

### DBpedia Entity Linking based on DBpedia Lookup service

The DBpedia Lookup is an entity retrieval service for Linked Data. It provides a straightforward solution for the frequent use case of resolving keywords and natural language to related resource identifiers in the DBpedia knowledge graph. Related means that either the label or abstract of a resource matches, or an anchor text that was frequently used in Wikipedia to refer to a specific resource matches (e.g. the resource http://dbpedia.org/resource/United_States can be looked up by the string “USA”).

So whether you need an auto-complete service for your RDF application, Linked Data enhancements for your CSV tables or simply a way to retrieve specific DBpedia identifiers – the DBpedia Lookup is for you!

As a part of the DBpedia Technology Stack the DBpedia Lookup can be deployed conveniently via Docker and works well with DBpedia Databus Collections. The DBpedia Lookup uses an Apache Lucene Index for resource indexing and retrieval and provides a web interface for querying._____no_output_____

import requests
def get_dbpedia_candidates(label):
    candidates = []
    if (label==""):
        return candidates
    # type: One of the following values: form, form, item, lexeme, property, sense, sense
    query_path = "http://lookup.dbpedia.org/api/search?query=QUERY_TEXT&maxResults=10&type=class&format=json"
    r = requests.get(query_path.replace("QUERY_TEXT",label))
    for answer in r.json()['docs']:
        candidate = {
            'label': answer['label'][0],
            'id':answer['resource'][0]
#            'description' : answer['display']['description']
        }
        candidates.append(candidate)
    return candidates

r = get_dbpedia_candidates("Berlín")
print(r)[{'label': 'Berlin', 'id': 'http://dbpedia.org/resource/Berlin'}, {'label': 'Bergen', 'id': 'http://dbpedia.org/resource/Bergen'}, {'label': 'Tennis Borussia Berlin', 'id': 'http://dbpedia.org/resource/Tennis_Borussia_Berlin'}, {'label': '1. FC Union Berlin', 'id': 'http://dbpedia.org/resource/1._FC_Union_Berlin'}, {'label': 'West Berlin', 'id': 'http://dbpedia.org/resource/West_Berlin'}, {'label': 'East Berlin', 'id': 'http://dbpedia.org/resource/East_Berlin'}, {'label': 'Berlín, Usulután', 'id': 'http://dbpedia.org/resource/Berlín,_Usulután'}, {'label': 'Humboldt University of Berlin', 'id': 'http://dbpedia.org/resource/Humboldt_University_of_Berlin'}, {'label': 'Hertha BSC', 'id': 'http://dbpedia.org/resource/Hertha_BSC'}, {'label': 'Carlos Berlín Montero', 'id': 'http://dbpedia.org/resource/Carlos_Berlín_Montero'}]