import cv2
import os
import sys
import numpy as np
import glob
import datetime

def main():
    # ポケモン数に関する定数
    pNumSum = 234   # ポケモン総数
    pNum1st = 214   # DLCなしのポケモン数
    pNum2nd = 234   # 1回目のDLC込のポケモン数
    pID1st = 464550 # DLCなしのCyberrecordのポケモンIDの最初
    pID2nd = 472761 # DLCありのCyberrecordのポケモンIDの最初

    os.chdir(os.path.dirname(os.path.abspath(__file__)))

    # ファイル名用に時刻取得
    now = datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9)))
    ymdhms = now.strftime("%Y%m%d%H%M%S")
    test =glob.glob("./images/*.jpg")

    # 結果入力の初期化
    result = []
    for i in range(pNumSum):
        result.append([0, 0, 0, 0])

    # ハイスコア画像のファイル名保存
    highImg = []
    for i in range(pNumSum):
        highImg.append(["", "", "", ""])

    with open("./" + ymdhms + "_detail.csv", "w") as f:
        # 画像読み込み
        for i, t in enumerate(test):
            img = cv2.imread(t, cv2.IMREAD_GRAYSCALE)
            zukanNo = getZukanNo(img)
            f.write(str(zukanNo))
            f.write(", ")
            starNum = getStar(img)
            f.write(str(starNum))
            f.write(", ")
            scoreSum = getScoreSum(img)
            f.write(str(scoreSum))
            # 複数枚同じ図鑑IDのポケモンの写真があった際に、その中での最大値を最高得点として記録する
            if result[zukanNo - 1][starNum - 1] < scoreSum:
                result[zukanNo - 1][starNum - 1] = scoreSum
                highImg[zukanNo - 1][starNum - 1] = os.path.split(t)[1]
            for j in range(6):
                f.write(", ")
                f.write(str(getEachScores(img, j)))
            f.write("\n")
            # 進捗
            sys.stdout.write("\r")
            sys.stdout.write("{}/{}".format(i, len(test)))
            sys.stdout.flush()
    with open("./" + ymdhms + "_sum.csv", "w") as f:
        f.write("No, 1-Star, 2-Star, 3-Star, 4-Star\n")
        for i in range(len(result)):
            f.write(str(i + 1))
            for j in range(len(result[0])):
                f.write(", ")
                if(result[i][j] == 0):
                    f.write("")
                else:
                    f.write(str(result[i][j]))
            f.write("\n")

    # cs書き出し用
    with open("./" + ymdhms + "_cs-script.txt", "w", encoding="utf-8") as f:
        f.write("* Score upload tool\n")
        f.write("Open Cyberscore by Chrome or Firefox of PC (Not IE or Safari)\n")
        f.write("https://cyberscore.me.uk/game/2785\n")
        f.write("Click \"+ Submit records\"\n")
        f.write("Check \"1★ Photos\" and click \"Edit selected records\"\n")
        f.write("Set \"Plat form:\" to \"Switch\".\n")
        f.write("Open Developer Tools (If enabled, F12) -> Open \"Console\" tab.\n")
        f.write("Copy the below codes and paste the console (Attention is shown if Firefox).\n")
        f.write("Click \"Save changes\"\n")
        f.write("Repeat  for \"2★ Photos\", \"3★ Photos\", \"4★ Photos\"\n\n")

        f.write("a=[")
        for i in range(len(result[0])):
            f.write("[")
            for j in range(len(result)):
                f.write("\"")
                if(result[j][i] != 0):
                    f.write(str(result[j][i]))
                f.write("\"")
                f.write(",")
            f.write("\"\"],")   # 面倒なので、,が残らないように0を入れておく
        f.write("[]")   # 面倒なので、,が残らないように空配列を入れておく

        f.write("];" + \
            "for(p=0;p<" + str(pNum1st) + ";p++){" + \
                "for(s=0;s<4;s++){" + \
                    "e=document.getElementsByName((" + str(pID1st) + "+p+s*" + str(pNum1st) + ")+'-input1')[0];" + \
                    "if(e){" + \
                        "e.value=a[s][p];" + \
                    "}" + \
                "}" + \
            "}" + \
            "for(p=" + str(pNum1st) +";p<" + str(pNum2nd) + ";p++){" + \
                "for(s=0;s<4;s++){" + \
                    "e=document.getElementsByName((" + str(pID2nd) + "+p-" + str(pNum1st) + "+s*" + str(pNum2nd - pNum1st) + ")+'-input1')[0];" + \
                    "if(e){" + \
                        "e.value=a[s][p];" + \
                    "}" + \
                "}" + \
            "}" + \
        "\n\n")
        f.write("* Proofs upload assist tool\n")
        f.write("Use FireFox or Chrome\n")
        f.write("Open \"Submit proofs\" -> \"Upload proofs from your device\"\n")
        f.write("-> Open \"Developer tool\" in your browser (F12 key)\n")
        f.write("-> Open \"Console\" tab -> Copy & paste the below codes\n")
        f.write("-> File button is shown below the navigation bar (Home, Games, Scoreboards, The site, Forum, Search[])\n")
        f.write("-> Upload all of images in \"images\" directory\n")
        f.write("-> Appropriate files are input into each row.\n")
        f.write("-> Click each \"Upload proof\" button manually\n\n")
        f.write("let arr = {")
        for i in range(len(highImg)):
            for j in range(len(highImg[0])):
                if(highImg[i][j] != ""):
                    if(i <= pNum1st):
                        f.write(str(pID1st + i + j * pNum1st))
                        f.write(":\"")
                        f.write(str(highImg[i][j]))
                        f.write("\",")
                    else:
                        f.write(str(pID2nd + i - pNum1st + j * (pNum2nd - pNum1st)))
                        f.write(":\"")
                        f.write(str(highImg[i][j]))
                        f.write("\",")
        f.write("0:\"\"};fileInput = document.createElement(\"input\");fileInput.type = \"file\";fileInput.multiple = true;fileInput.addEventListener(\"change\", e => {const {files} = e.target;for(let i = 0; i < document.forms.length; i++){let tempForm = document.forms[i];if(!tempForm.chart_id){continue;}const id = tempForm.chart_id.value;if(id in arr){const input = tempForm.proof_file;const fileName = arr[id];for(let j = 0; j < files.length; j++){let file = files[j];if(file.name == fileName){const dt = new DataTransfer();dt.items.add(file);input.files = dt.files;break;}}}}}, false);const pageRoot = document.getElementById(\"pagefull\");pageRoot.insertBefore(fileInput, pageRoot.firstChild);\n\n")
        f.write("* \"Upload proof\" automatic click tool\n")
        f.write("This tool may not work properly depending on your environment, \nso I do not provide any support for it.\n")
        f.write("Disable popup blocker.\n")
        f.write("Replace \"1\" in the first line of the below script with \"(the number of seconds it takes to upload) + 3\".\n")
        f.write("Use the developer tool of Firefox, not Chrome\n")
        f.write("Attention: Tremendous tabs will be opened, so close them accordingly.\n")
        f.write("\n")
        f.write("let interval = 1;\nfor(let i = 0; i < document.forms.length; i++){\n    let tempForm = document.forms[i];\n    if(!tempForm.chart_id){\n        continue;\n    }\n    setTimeout(\n        function(f){\n            f.click();\n        }\n    , interval * 1000 * i, tempForm[tempForm.length - 1]);\n}\n")

def getStar(img):
    ## 星の数を検出
    # 切り出し img[top : bottom, left : right]
    img_stars = img[64 : 64 + 30, 199 : 199 + 122]

    # 中埋め
    cv2.floodFill(img_stars, None, (0, 0), (0, 0, 0), loDiff=(50, 50, 50), upDiff=(50, 50, 50), flags=(4 | cv2.FLOODFILL_FIXED_RANGE))

    ## 2値化
    threshold = 0
    _, img_binary = cv2.threshold(img_stars, threshold, 255, cv2.THRESH_BINARY)

    # 星の数を算出
    contours, _ = cv2.findContours(img_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    return len(contours)

def getZukanNo(img):
    # 特徴点抽出時に、余白がないとだめだから、その余白の大きさ
    yohaku = 10
    # 文字位置
    position = [725, 734, 743]
    widthNum = 10
    os.chdir(os.path.dirname(os.path.abspath(__file__)))
    descriptors = [
        np.load("./Descriptors/ZukanNo_0.npy"),
        np.load("./Descriptors/ZukanNo_1.npy"),
        np.load("./Descriptors/ZukanNo_2.npy"),
        np.load("./Descriptors/ZukanNo_3.npy"),
        np.load("./Descriptors/ZukanNo_4.npy"),
        np.load("./Descriptors/ZukanNo_5.npy"),
        np.load("./Descriptors/ZukanNo_6.npy"),
        np.load("./Descriptors/ZukanNo_7.npy"),
        np.load("./Descriptors/ZukanNo_8.npy"),
        np.load("./Descriptors/ZukanNo_9.npy")
    ]
    img = cv2.bitwise_not(img)

    imgs = []
    for i in range(len(position)):
        img_temp = img[144 : 161, position[i] : position[i] + widthNum ]
        img_temp = cv2.copyMakeBorder(img_temp, yohaku, yohaku, yohaku, yohaku, cv2.BORDER_CONSTANT,value=[255,255,255])
        imgs.append(img_temp)
    return predict(imgs, descriptors)

def getEachScores(img, row):
    # 特徴点抽出時に、余白がないとだめだから、その余白の大きさ
    yohaku = 10
    # 文字位置
    position = [1005, 1023, 1035, 1047]
    widthNum = 13
    os.chdir(os.path.dirname(os.path.abspath(__file__)))
    descriptors = [
        np.load("./Descriptors/ScoreSmall_0.npy"),
        np.load("./Descriptors/ScoreSmall_1.npy"),
        np.load("./Descriptors/ScoreSmall_2.npy"),
        np.load("./Descriptors/ScoreSmall_3.npy"),
        np.load("./Descriptors/ScoreSmall_4.npy"),
        np.load("./Descriptors/ScoreSmall_5.npy"),
        np.load("./Descriptors/ScoreSmall_6.npy"),
        np.load("./Descriptors/ScoreSmall_7.npy"),
        np.load("./Descriptors/ScoreSmall_8.npy"),
        np.load("./Descriptors/ScoreSmall_9.npy"),
    ]

    imgs = []
    for i in range(len(position)):
        img_temp = img[243 + int(row * 31.4) : 243 + int(row * 31.4) + 20, position[i] : position[i] + widthNum ]
        img_temp = cv2.copyMakeBorder(img_temp, yohaku, yohaku, yohaku, yohaku, cv2.BORDER_CONSTANT,value=[255,255,255])
        imgs.append(img_temp)
    return predict(imgs, descriptors)

def getScoreSum(img):
    # 特徴点抽出時に、余白がないとだめだから、その余白の大きさ
    yohaku = 10
    # 文字位置
    position = [968, 999, 1020, 1041]
    widthNum = 21
    os.chdir(os.path.dirname(os.path.abspath(__file__)))
    descriptors = [
        np.load("./Descriptors/ScoreLarge_0.npy"),
        np.load("./Descriptors/ScoreLarge_1.npy"),
        np.load("./Descriptors/ScoreLarge_2.npy"),
        np.load("./Descriptors/ScoreLarge_3.npy"),
        np.load("./Descriptors/ScoreLarge_4.npy"),
        np.load("./Descriptors/ScoreLarge_5.npy"),
        np.load("./Descriptors/ScoreLarge_6.npy"),
        np.load("./Descriptors/ScoreLarge_7.npy"),
        np.load("./Descriptors/ScoreLarge_8.npy"),
        np.load("./Descriptors/ScoreLarge_9.npy")
    ]

    imgs = []
    for i in range(len(position)):
        img_temp = img[199 : 233, position[i] : position[i] + widthNum ]
        img_temp = cv2.copyMakeBorder(img_temp, yohaku, yohaku, yohaku, yohaku, cv2.BORDER_CONSTANT,value=[255,255,255])
        imgs.append(img_temp)
    return predict(imgs, descriptors)

def predict(img, img_temp):
    result = 0
    threshold = 150
    for i, target in enumerate(img):
        predict = 0
        # 文字が存在するかの判定用に2値化
        _, isBlank = cv2.threshold(target, threshold, 255, cv2.THRESH_BINARY)
        if(np.any(cv2.bitwise_not(isBlank))):    # 文字があるかの判定
            score = 0
            for j, template in enumerate(img_temp):
                _, maxVal, _, _ = cv2.minMaxLoc(cv2.matchTemplate(target, template, cv2.TM_CCOEFF_NORMED))
                if(score < maxVal):
                    score = maxVal
                    predict = j
        result = result + predict * pow(10, len(img) - i - 1)
    return result

if __name__ == "__main__":
    main()


#!/usr/bin/env python
# coding: utf-8

# In[1]:

import numpy as np
import scipy.special as sci
import matplotlib.pyplot as plt
from scipy import stats # linregress
import pandas as pd
from IPython.display import Latex

# # Lecture 11:  Background of Groundwater Modeling
#
#
# _(The contents presented in this section were re-developed principally by Dr. P. K. Yadav. The original contents are from Prof. Rudolf Liedl)_
#
# ---
#
# ## Motivation ###
#
# This lecture introduces the realm of mathematical modeling realm in groundwater studies. In the previous lectures the fundamental quantities, their properties and approach to quantify them were discussed. Those information were then used to develop system equations for varieties of groundwater problems. It was discussed then that these system equations require mathematical approaches the theory for which have to be systematically discussed and understood. Groundwater modeling can then be described as the systematic use of mathematical approaches leading to solution of the groundwater problem.
#
#
# Groundwater modeling is often the first step towards understanding and solving groundwater problems/issues. Groundwater modeling is a very broad topic, this and the following lectures only introduces fundamental part of groundwater modeling. In this course we focus on groundwater flow problems.

# ## Introduction ##
#
# ### What is a Model? ###
#
# Very succinctly a  **model** is a representation, which may be an image or a description of a real system. The description can be of different form (e.g., scales), of different level of detail (e.g., conceptual versus mathematical). The **system** to be modeled can be a **real** or also **conceptual**. A very relevant example of a real system for this course is the Darcy's experiment (see figure below), in which water is made  to flow through the porous media.
#
#
# ```{figure} images/M11_f1.png
# ---
# scale: 60%
# align: center
# name: Darcy
# ---
# Darcy's experimental setup<sup>[^Darcy(1856)]</sup>
# ```
# With Darcy's experiment, one could set-up a mathematical model to relate flow rate and hydraulic gradient ($h$). As model is _only an image_ of the real system, several assumptions have to be made in it's development. At many instances the model can not be set without these assumptions. In other cases solution of the model may not be possible without these assumptions being part of the model development. Darcy‘s Law, for instance, does not provide an exact representation of flow through individual pore channels. Rather, **average** flow behaviour through **many** pore channels is represented.
#
#
# [^Darcy(1856)]: Darcy, H., Les Fontaines Publiques de la Ville de Dijon, Dalmont, Paris, 1856.
#

# ### Model Types: Process-Based and Empirical Models ###
#
# Model can be classified in many ways. The following two types of classification are a more general way to classify models:
#
# > 1. **Conceptual models**
#
# > 2. **Process-Based and Empirical Models**
#
# > 3. **Mathematical models**
#
# #### Conceptual models ####
#
# The **conceptual models** is classification of models that distinguishes the qualitative from the quantitative description of a real system. A **conceptual model** provides a qualitative representation of the relevant system components, processes, and impacts in the area of investigation. This representation is usually shown graphically, e.g., as block models (see figure {ref}`Cmodel`). It will be shown later that conceptual model in fact is the first block in the development of a mathematical model.
#
# ```{figure} images/M11_f3.png
# ---
# scale: 20%
# align: center
# name: Cmodel
# ---
# A conceptual model showing different components of a hydrological system that can impact groundwater.
# ```
#
#
# #### Physically based models and Empirical models ####
#
# The **physically based models** also referred to as **process-based** models are models that exclusively relies on the fundamental physical laws - e.g., law of conservation of mass, energy, volume. _Compartment_  based models are example of physically based models.
#
# Contrary to use of fundamental physical laws, **empirical models** are developed on the basis on experimental/collected data. Sorption isotherms that were developed in lecture [(10)](/contents/transport/lecture_10/22_reactive_transport) are types of emperical models. As was with the isotherms, these types of models are often based on regression analysis. Figure below show a prediction (blue) line as a predictor of data.
#

# In[2]:

from myst_nb import glue
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression

# x from 0 to 30
x = 30 * np.random.random((20, 1))

# y = a*x + b with noise
y = 0.2 * x + 3.0 + np.random.normal(size=x.shape)

# create a linear regression model
model = LinearRegression()
model.fit(x, y)

# predict y from the data
x_new = np.linspace(0, 30, 100)
y_new = model.predict(x_new[:, np.newaxis])

# plot the results
fig, ax = plt.subplots(figsize=(6, 4))
ax.scatter(x, y, c="red", label = "data")
ax.plot(x_new, y_new, label = "prediction")

ax.set_ylabel(r'Discharge (m$^3$/s)')
ax.set_xlabel('Hydraulic Head (m)')

ax.axis('tight')
ax.legend()

plt.close(fig)
glue("em_fit", fig, display=False )

# ```{glue:figure} em_fit
# :figwidth: 600px
# :name: "empirical"
#
# Empirical relation between hydraulic head and discharge.
# ```
#
# In certain cases **hybrid models**, e.g. **semi-empirical** models can also occur. These models combines the components of empirical/numerical and analytical models. Darcy's law in fact is a semi-empirical model. On the one hand, it is based on the momentum conservation.  On the other hand, it is not possible to _strictly_ deduce the direct proportionality between flow rate and hydraulic gradient by averaging the flow behaviour over all pores.
#

# #### Mathematical models ####
#
# A **mathematical model** provides a quantitative representation of the relevant system components (described by for e.g., conceptual model),processes (e.g., described by physical model) and impacts in the area of investigation. The quantitative representation is based on mathematical equations. The system equations, that were developed and discussed in Lecture [(7)](/contents/flow/lecture_07/17_quantify_flow) are mathematical models.
#
# System equation or mathematical models can in certain cases be solved directly resulting to an _exact solution_ called **analytical solution**. Theis equation that was developed in Lecture [(8)](/contents/flow/lecture_08/18_wells) to quantify aquifer drawdown resulting from pumping of groundwater is an example of an analytical solution.
#
# For more complex problems, often a more natural groundwater conditions, only _approximate_ (or non-exact) solution called **numerical solution** can be  obtained. Numerical solutions are obtained after converting the system equation to so called **numerical models**.
#
# Our focus in this introductory modeling lecture is to understand the development and solution of _numerical model_ of simple groundwater flow problems.
#
#

# ### Example of an Analytical Solution ###
#
# Consider a conceptual model presented in the Figure {ref}`Ditch`. The unconfined aquifer separates two surface exposed water bodies. The water body in the left has a higher hydraulic head ($h_0$, [L]) compared to that on the right ($h_u$). Thus the flow of water is from left water body to the right one along the separating aquifer. In this scenario one of the problem to address will be to understand how the aquifer reacts to change in heads of water bodies. Additionally, how additional water, e.g., from precipitation/recharge ($N$, [L/T]), will effect the aquifer water.
#
# ```{figure} images/M11_f4.png
# ---
# scale: 20%
# align: center
# name: Ditch
# ---
# Conceptual model of a flow between two water bodies separated by unconfined aquifer
# ```
# The conceptual problem can be addressed when assumptions such as steady condition prevails, Darcy's law in aquifer is valid, recharge rate are relatively low. Based on the these assumptions, one of the **mathematical model** of this conceptual problem is:
#
# $$
# \frac{\textrm{d}}{\textrm{d}x}\bigg(-h \cdot K\cdot \frac{\textrm{d}h}{\textrm{d}x}  \bigg) = N
# $$
#
# and the 2 boundary conditions:
#
# $$
# h(0) = h_0 \:\: \text{and} \:\: h(L) = h_L
# $$
#
# Note that a complete formulation of mathematical model requires accompanying boundary conditions. The boundary conditions are used to uniquely define the problem. An _analytical solution_ for this mathematical model and accompanying boundary condition is:
#
# $$
# h(x) = \sqrt{h_o^2 - (h_o^2 - h_L^2)\cdot \frac{x}{L} + \frac{N}{K}\cdot x \cdot (L-x) }
# $$
#
# The solution can be used to quantify change in aquifer head $h$ with different system quantities, e.g., conductivity $K$ [L/T], recharge $N$ [L/T] water body heights $h_0,\, h_L$ [L] along the flow direction.
#
# The additional tool: _Conservative Transport_ ([TOOLS](/contents/tools/1D_ditchflow)) interactively simulates the ditch flow concept in more details.

# ### Example problem  ###
#
# ```{admonition} Ditch flow
# Explore the effect of recharge ($N= 0$ and $N= 0.1$ mm/d) on the aquifer level for the conceptual problem provided above. Other required data are provided below.
# The effect are to be explored at mid of the aquifer
# ```
#
#

# In[3]:

print("Provided are:\n")

K = 2E-4 # hydraulic conductivity [m/s]
Ho = 10 # head at the origin [m]
Hu = 7.5 # head at L [m]
L = 175 #flow length [m]
N1 = 0 # no recharge [m/s]
N2 = 1000 # recharge [mm/a]

# intermediate calculation
x = L/2 # mid of the aquifer [m]
N_ = N2/1000/365/86400 # recharge, [m/s]

#solution
h1=(Ho**2-(Ho**2-Hu**2)/L*x+(N1/K*x*(L-x)))**0.5
h2=(Ho**2-(Ho**2-Hu**2)/L*x+(N_/K*x*(L-x)))**0.5

print("hydraulic conductivity = {} m\nhead at origin = {} m\nhead at L = {} m\nflow length = {} m\nRecharge = {} mm/a".format(K, Ho, Hu, L, N2 ),"\n")
print("The resulting head without head is {:0.2f} m \n".format(h1))
print("The resulting head with head is {:0.2f} m \n".format(h2))

# ### Example for a Model without Analytical Solution ###
#
# Analytical solutions are rather an exception. Natural aquifer or groundwater system are more complex (see figure below) and therefore analytical solution are not possible. The complexity in natural system are due to parameter heterogeneity and irregular model domain boundaries, and these must be included in underlying model equation.
#
#
# ```{figure} images/M11_f5.png
# ---
# scale: 60%
# align: center
# name: nummodel
# ---
# The numerical model of a natural aquifer
# ```
#
#
#

# ## Conceptual Model to Numerical Approach ##
#
# The first step in modeling is to establish the purpose of the model. With that established, the development of conceptual model begins the set-up of the numerical model. This is a step-wise process that includes:
#
# > 1. **Conceptual model** - providing hydrogeological units within the model domain
#
# > 2. **Water budgeting** - identifying water containing units and characterizing it
#
# > 3. **Numerical model** - Combining the units of conceptual model, water budgeting components and imposing _boundary conditions._
#
# ```{figure} images/M11_f6.png
# ---
# scale: 60%
# align: center
# name: nummodel
# ---
# The numerical model of a natural aquifer
# ```
#
# The water budgeting part should include the sources of water inflowing to the system including the expected flow directions and exiting water. Estimate of Groundwater recharge, overland flow etc, are few examples of inflows. Likewise, estimate of baseflow to streams, evapotranspiration, abstraction wells etc. are outflows. Field data are required to prepare water budget.
#
# The type of numerical model, depending on the modeling goal and required/available data, is then decided. The model type is mostly time-related (transient, steady) and dimension-related (1D,2D, 3D).

# ### Data Requirements ###
#
# Numerical model are often very data intensive. Several types of data of different origin are required in the development of a numerical model. These data come from site data records/maps, field works, lab works and the ancillary mathematical analysis of the field and lab data. Overall, the required data can be:
#
# + topographical maps (with surface waters and water divides)
# + geological maps, geological profiles (see figure {ref}`nummodel` example)
# + maps with isolines of aquifer bottoms / thicknesses , aquitard bottoms / thicknesses
# + maps indicating vertical extensions of sediments under rivers and lakes
# + hydrogeological maps (hydraulic head isolines)
# + water level time series in observation wells and rivers
# + time series of spring discharges
# + maps and profiles of hydraulic conductivity or transmissivity (also for river / lake sediments mentioned above)
# + maps and profiles of storage coefficients
# + information on spatial and temporal variability of inflow / outflow due to
#   - groundwater recharge
#   - evapotranspiration
#   - interaction between groundwater and surface water
#   - groundwater abstraction
#   - natural groundwater flow
#

# ## Example of a Groundwater Model ##
#
# Let us now develop an example flow model. To begin with we set-up an overly simplified model that still will require a numerical solution.
#
# **Step 1** - Spatial Extension
# + Horizontal extension along $x-$direction: 4000 m
# + Horizontal extension along $y-$direction: 2500 m
# + vertical extension along $z$-direction: from $z = 250$ m a.s.l. at the aquifer bottom to $z= 265$ m at the aquifer top.
# + aquifer thickness is uniform = 15 m.
#
# Putting these information graphically, we get the following schematic:
#
# ```{figure} images/M11_f7.png
# ---
# scale: 40%
# align: center
# name: nummodel_ex
# ---
# Spatial extension of an example numerical model
# ```
# The example model is 2D and we _assume_ that vertical flow components can be neglected despite the recharge vertically entering the groundwater. These kinds of simplification are quite common in the development of the numerical model. These simplifications have to be justified when presenting model results.
#
# **Step 2** - Hydraulic Properties
#
# For our example model we consider the following:
#
# + effective porosity ($\eta_e$) in the model domain: 0.2 or 20%
# + two zones with different hydraulic conductivities ($K$)
# + two zones with different groundwater recharge
# + A section of a river (_river reach_) is in hydraulic contact with the aquifer. i.e. there may be water transfer from the river to the aquifer (_influent conditions_) or vice versa (_effluent conditions_).
# + inflow boundary with prescribed hydraulic heads
# + outflow boundary with prescribed hydraulic heads
# + two impermeable boundaries
#
# **Step 3** - The model purpose and conceptual model
#
# From our example model we intend to:
#
# + Abstraction of groundwater through wells is planned in the area with an overall pumping rate of 7000 m$^3$/d.
#
# + Water extraction is to be distributed between two wells located at $(x,y) =$ (3050 m, 1550 m) and $(x,y)$ = (3050 m, 1450 m), resp.
#
# + The model purpose is to outline the 50-days isochrone for both wells.
#
# With these information available, our conceptual model takes the following form:
#
# ```{figure} images/M11_f8.png
# ---
# scale: 25%
# align: center
# name: Concept_model
# ---
# The conceptual model of the example model
# ```
# **Step 4** - The numerical approach
#
# This is discussed in the next lecture.

#


# -*- coding: utf-8 -*-
"""Customer Segmentation in Online Retail.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1nmqPZGcVGac_lPyVR2C--AzHhm_2T43X

# Customer Segmentation in Online Retail
---
"""

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import warnings

from google.colab import drive
warnings.filterwarnings('ignore')

"""## 1. Data Preparation"""

drive.mount('/content/drive/')
df=pd.read_excel('/content/drive/MyDrive/datasets/Online_Retail.xlsx')

"""I load the data. Once done, I also give some basic informations on the content of the dataframe: the type of the various variables, the number of null values and their percentage with respect to the total number of entries:"""

df.head()

df.describe().T

df.isna().sum()

### Checking for Missing values
pd.DataFrame(data={'Data Type':df.dtypes.values,'Null Values':df.isna().sum().values,
        '%age of Null Values':[round(df.isna().sum().values[i]*100/len(df),2) for i in range(len(df.columns))]},index=df.columns).T

"""While looking at the number of null values in the dataframe, it is interesting to note that  ∼ 25% of the entries are not assigned to a particular customer. With the data available, it is impossible to impute values for the user and these entries are thus useless for the current exercise. So I delete them from the dataframe:"""

df[df.Description.isna()==True].sort_values(by='UnitPrice',ascending=False)

"""`We can see that the data point with the values missing are just some faulty entries with zero Unit Price. So we can delete them`"""

df.dropna(subset=['CustomerID'],axis=0,inplace=True)
pd.DataFrame(data={'Data Type':df.dtypes.values,'Null Values':df.isna().sum().values,
        '%age of Null Values':[round(df.isna().sum().values[i]*100/len(df),2) for i in range(len(df.columns))]},index=df.columns).T

### Checking for Duplicates
print('The number of Duplicates are ',df.duplicated().sum())
df[df.duplicated()==True]

df.drop_duplicates(inplace=True)

df.duplicated().sum()

"""## 2. Exploring the Data

### `Country`
"""

df.Country.unique()

df.Country.value_counts()

plt.figure(figsize=(18,8))
sns.countplot(df.Country,order=df.Country.value_counts(ascending=False).index[:10],palette='Accent')
plt.xticks(rotation=45)
plt.title('Top 10 Countries in terms of no of orders')
plt.show()

"""The above graph shows the percentage of orders from the top 10 countries, sorted by the number of orders. This shows that more than 90% of orders are coming from United Kingdom and no other country even makes up 3% of the orders in the data.

Therefore, for the purpose of this analysis, I will be taking data corresponding to orders from the United Kingdom. This subset will be made in one of the next steps and will be mentioned as required.

### `Customers and Products`
"""

#Let us now look at the total number of products, transactions, and customers in the data,
#which correspond to the total unique stock codes, invoice number, and customer IDs present in the data.

pd.DataFrame([{'Products': len(df['StockCode'].value_counts()),
               'Transactions': len(df['InvoiceNo'].value_counts()),
               'Customers': 4372,
              }], index = ['Quantity'])

"""It can be seen that the data concern 4372 users and that they bought 3958 different products. The total number of transactions carried out is of the order of  ∼ 22'000.

will determine the number of products purchased in every transaction:
"""

temp=df.groupby('InvoiceNo',as_index=False).agg(
    {'Description':np.count_nonzero}).sort_values(by='Description',ascending=False)
temp.columns=['InvoiceNo','Total_Orders']
temp

"""The first lines of this list shows several things worthy of interest:

1. the existence of entries with the prefix C for the InvoiceNo variable: this indicates transactions that have been canceled
2. the existence of users who only came once and only purchased one product (e.g. nº12346)
3. the existence of frequent users that buy a large number of items at each order
"""

## Order Distribution per Invoice
plt.figure(figsize=(10,5))
plt.hist(temp.Total_Orders,bins=40)
plt.xlabel('Invoice')
plt.ylabel('Total Orders')
plt.show()

temp['check']=[str(np.where(str.startswith(str(k),'C')==False,'Not Cancelled','Cancelled')) for k in temp.InvoiceNo]

print(temp.check.value_counts())

sns.countplot(temp.check)
plt.grid()
plt.plot()

print('\nAround {}% of orders were cancelled'.format(round(temp.check.value_counts()[1]*100/len(temp),2)))

"""We note that the number of cancellations is quite large ( ∼ 16% of the total number of transactions)."""

df.head()[:2]

"""### `Stock Code`"""

temp=pd.DataFrame(data={'StockCode':df.StockCode,'Description':df.Description,'dtype':[str.isidentifier(str(k)) for k in df.StockCode]})
temp[temp.dtype==True][:5]

temp[temp.dtype==True].StockCode.unique()

codes=['POST', 'D', 'C2', 'M', 'PADS', 'DOT', 'CRUK']
temp=temp[temp.StockCode.isin(codes)]
for code in codes:
    print("{:<15} -> {:<30}".format(code,temp[df.StockCode==code].Description.unique()[0]))

"""We see that there are several types of peculiar transactions, connected i.e., port charges, bank fee, discount, free gifts,etc"""

plt.figure(figsize=(10,5))
sns.countplot(temp.StockCode)
plt.xticks(rotation=60)
plt.show()

"""## `Total Amount per Billing`

In order to have a global view of the type of order performed in this dataset, I determine how the purchases are divided according to total Amount
"""

df['Amount']=df.UnitPrice*df.Quantity
df.head()

temp=df[(df.Quantity>0) & (df.StockCode.isin(codes).values==False)].groupby(
    'InvoiceNo',as_index=False).agg(
    {'Quantity':sum,'Amount':sum})
temp.head()

plt.figure(figsize=[18,5])
sns.distplot(temp[temp!=0].Amount,kde=False,)

"""Maximum orders are under $12500"""

bins = [-1,50,100,200,500,1000,5000,np.inf]
names = ['<50','50-100','100-200','200-500','500-1000','1000-5000','5000+']
temp['amount_cat']=pd.cut(temp['Amount'],bins,labels=names)

plt.figure(figsize=(7,7))
plt.pie(temp[temp.Amount>0].amount_cat.value_counts().values,labels=names,autopct = lambda x:'{:1.0f}%'.format(x) if x > 1 else '',
       shadow = True, startangle=0)
plt.show()

"""It can be seen that the vast majority of orders concern purcheses of low value ∼ 78% of purchases give prices in excess of £200."""

temp=df[(df.Quantity>0) & (df.StockCode.isin(codes).values==False)].groupby(
    'Country',as_index=False).agg(
    {'Quantity':sum,'Amount':sum})
temp.head()

plt.figure(figsize=(18,5))
sns.barplot(x='Country',y='Amount',data=temp[temp.Country!='United Kingdom'],)
plt.xticks(rotation=45)
plt.title('Country-wise Sales(UK not included)',size=15)
plt.show()

"""### I thought of implementing nltk to `create product categories` but it can happen that a particular category of product wasn't sold for the whole year. So generalising things to segregate all products in this clusters won't prove to be a good method

## 3. Understanding Cohort Analysis

What is Cohort Analysis?

A cohort is a set of users who share similar characteristics over time. Cohort analysis groups the users into mutually exclusive groups and their behaviour is measured over time.

It can provide information about product and customer lifecycle.

There are three types of cohort analysis:

1. Time cohorts: It groups customers by their purchase behaviour over time.
2. Behaviour cohorts: It groups customers by the product or service they signed up for.
3. Size cohorts: Refers to various sizes of customers who purchase company's products or services. This categorization can be based on the amount of spending in some period of time.

Understanding the needs of various cohorts can help a company design custom-made services or products for particular segments.

In the following analysis, we will create Time cohorts and look at customers who remain active during particular cohorts over a period of time that they transact over.

### `Time Cohorts`

`Checking the date range of our data, we find that it ranges from the start date: 2010–12–01 to the end date: 2011–12–09.`

`Next, a column called CohortMonth was created to indicate the month of the transaction by taking the first date of the month of InvoiceDate for each transaction. Then, information about the first month of the transaction was extracted, grouped by the CustomerID.`
"""

import datetime as dt

# Start and end dates:
print('Start date: {}'.format(df.InvoiceDate.min()))
print('End date: {}'.format(df.InvoiceDate.max()))

cohort_data = df[['InvoiceNo','StockCode','Description','Quantity','InvoiceDate','UnitPrice','Amount','CustomerID','Country']]

cohort_data.isna().sum()

cohort_data.InvoiceDate=pd.to_datetime(cohort_data.InvoiceDate).apply(lambda x:dt.datetime(x.year,x.month,x.day))

cohort_data.head()[:2]

grouping=cohort_data.groupby('CustomerID',as_index=False)['InvoiceDate'].min()
grouping.columns=['CustomerID','CohortMonth']
grouping['CohortMonth']=grouping['CohortMonth'].apply(lambda x:dt.datetime(x.year,x.month,1))
grouping.columns=['CustomerID','CohortMonth']
cohort_data=cohort_data.merge(grouping,on='CustomerID',how='left')
cohort_data.head()

cohort_data['CohortIndex']=pd.Series((cohort_data.InvoiceDate-cohort_data.CohortMonth)/30).dt.days.astype('int')

cohort_data.tail()

grouping=cohort_data.groupby(['CohortMonth','CohortIndex'],as_index=False).agg({'CustomerID':'nunique'})
cohort_counts=grouping.pivot_table(columns='CohortIndex',index='CohortMonth')
cohort_counts

"""What does the above table tell us?

Consider CohortMonth 2010–12–01: For CohortIndex 0, this tells us that 815 unique customers made transactions during CohortMonth 2010–12–01. For CohortIndex 1, this tells that there are 289 customers out of 815 who made their first transaction during CohortMonth 2010–12–01 and they also made transactions during the next month. That is, they remained active.

For CohortIndex 2, this tells that there are 263 customers out of 815 who made their first transaction during CohortMonth 2010–12–01 and they also made transactions during the second-next month. And so on for higher CohortIndices.

Let us now calculate the Retention Rate. It is defined as the percentage of active customers out of total customers. Since the number of active customers in each cohort corresponds to the CohortIndex 0 values, we take the first column of the data as the cohort sizes.
"""

cohort_sizes = cohort_counts.iloc[:,0]

# Divide all values in the cohort_counts table by cohort_sizes
retention=cohort_counts.divide(cohort_sizes, axis=0)

# Review the retention table
retention=retention.round(3)*100
retention

plt.figure(figsize=(10, 8))
sns.heatmap(retention,annot=True,fmt = '0.1f',cmap ='BuGn')
plt.title('Retention rates')
plt.show()

"""# 4. RFM Segmentation

RFM stands for Recency, Frequency, and Monetary.

`RFM analysis is a commonly used technique to generate and assign a score to each customer based on how recent their last transaction was (Recency), how many transactions they have made in the last year (Frequency), and what the monetary value of their transaction was (Monetary).`

`RFM analysis helps to answer the following questions: Who was our most recent customer? How many times has he purchased items from our shop? And what is the total value of his trade? All this information can be critical to understanding how good or bad a customer is to the company.`

`After getting the RFM values, a common practice is to create ‘quartiles’ on each of the metrics and assigning the required order.`

We will be using Amount column to get the monetary value of each transaction. Calling the .describe() method on this column, we get:
"""

cohort_data.head()[:3]

"""The defination of recency takes into consideration a complete one year data. So, we'll crop out one year from the end of data"""

print("The Date Range we'll select will be\n")
start_date=cohort_data.InvoiceDate.max()-dt.timedelta(days=364)
print('Start Date: ',start_date)
print('End Date: ',cohort_data.InvoiceDate.max())

data_rfm=cohort_data[(cohort_data.InvoiceDate>=start_date) & (cohort_data.Amount>0)]
data_rfm.reset_index(drop=True,inplace=True)
data_rfm.head()

"""`Now, for RFM analysis, we need to define a ‘snapshot date’, which is the day on which we are conducting this analysis. Here, I have taken the snapshot date as the highest date in the data + 1 (The next day after the date till which the data was updated). This is equal to the date 2011–12–10. (YYYY-MM-DD)`"""

snapshot_date=data_rfm.InvoiceDate.max()+dt.timedelta(days=1) #The date at which the RFM Analysis should be taking place
print('Snapshot Date: ',snapshot_date)

# Aggregate data on a customer level

data=data_rfm.groupby('CustomerID',as_index=False).agg({'InvoiceDate':lambda x:(snapshot_date-x.max()).days,
                                        'InvoiceNo':'count',
                                        'Amount':'sum'}).rename(columns = {'InvoiceDate': 'Recency',
                                                                                'InvoiceNo': 'Frequency',
                                                                                'Amount': 'MonetaryValue'})
data.head()

"""`For the recency metric, the highest value, 4, will be assigned to the customers with the least recency value (since they are the most recent customers). `

`For the frequency and monetary metric, the highest value, 4, will be assigned to the customers with the Top 25% frequency and monetary values, respectively.`

`After dividing the metrics into quartiles, we can collate the metrics into a single column (like a string of characters {like ‘213’}) to create classes of RFM values for our customers. We can divide the RFM metrics into lesser or more cuts depending on our requirements.`
"""

r_quartiles=pd.qcut(data.Recency,4,labels=[4,3,2,1])
f_quartiles=pd.qcut(data.Frequency,4,labels=[1,2,3,4])
m_quartiles=pd.qcut(data.MonetaryValue,4,labels=[1,2,3,4])

data['R']=r_quartiles
data['F']=f_quartiles
data['M']=m_quartiles

data.head()

data['RFM_Segment']=[str(data.R[i])+str(data.F[i])+str(data.M[i]) for i in range(len(data))]
data['RFM_Score']=data.R.astype('int')+data.F.astype('int')+data.M.astype('int')

data.head()

"""Let us now analyse RFM Score distribution and their groups."""

data.groupby('RFM_Score').agg({'Recency': 'mean',
                                'Frequency': 'mean',
                                'MonetaryValue': ['mean', 'count'] }).round(1)

"""As expected, customers with the lowest RFM scores have the highest recency value and the lowest frequency and monetary value, and the vice-versa is true as well.

#### `Finally, we can create segments within this score range of RFM_Score 3–12, by manually creating categories in our data:`
1. Customers with an RFM_Score greater than or equal to 9 can be put in the ‘Top’ category.
2. Similarly, customers with an RFM_Score between 5 to 9 can be put in the ‘Middle’ category, and
3. the rest can be put in the ‘Low’ category.

Let us call our categories the ‘General_Segment’. Analyzing the mean values of recency, frequency, and monetary, we get:
"""

bins=[0,5,9,np.inf]
label=['Low','Middle','Top']
data['General_Segment']=pd.cut(data.RFM_Score,bins=bins,labels=label)

data.groupby('General_Segment').agg({'Recency':'mean',
                                    'Frequency':'mean',
                                    'MonetaryValue':['mean','count']}).round(1)

"""### In many scenarios, this would be okay. But, if we want to properly find out segments on our RFM values, we can use a clustering algorithm like K-means.

## Preprocessing data for Clustering

`In the next section, we are going to prepare the data for Kmeans clustering on RFM Score data. To do this, we need to preprocess the data so that it can meet the key assumptions of Kmeans algorithm, which are:`

`1. The varaiables should be distributed symmetrically
2. Variables should have similar average values
3. Variables should have similar standard deviation values`
"""

# Checking the distribution of Recency, Frequency and MonetaryValue variables.
plt.figure(figsize=(18,10))

# Plot distribution of var1
plt.subplot(3, 1, 1); sns.distplot(data['Recency'],bins=50)

# Plot distribution of var2
plt.subplot(3, 1, 2); sns.distplot(data['Frequency'],bins=100)

# Plot distribution of var3
plt.subplot(3, 1, 3); sns.distplot(data['MonetaryValue'],bins=100)

plt.show()

"""As we can see from the above plots, all the variables do not have a symmetrical distribution. All of them are skewed to the right. To remove the skewness, we can try the following transformations:

1. log transformations
2. Box-Cox transformations
3. Cube root transformations

The log transformation cannot be used for negative values. One common practice one can use here is to add a constant value to get a positive value and this is generally taken as the absolute of the least negative value of the variable to each observation. However, in our data, we do not have any negative values since we are dealing with customer transactions dataset.
"""

data[['Recency', 'Frequency', 'MonetaryValue']].describe().T

"""Min Value of every column > 0

Also, We also see that we do not get constant mean and standard deviation values.
"""

data.sample(5)

rfm_data = data[['Recency','Frequency','MonetaryValue']]

from sklearn.preprocessing import StandardScaler

# Unskew the data
data_log = np.log(rfm_data)

# Initialize a standard scaler and fit it
scaler = StandardScaler()
scaler.fit(data_log)

# Scale and center the data
data_normalized = scaler.transform(data_log)

# Create a pandas DataFrame
data_norm = pd.DataFrame(data=data_normalized, index=rfm_data.index, columns=rfm_data.columns)

data_norm.describe().round(2).T

"""Will check for skewness in Data now again"""

plt.figure(figsize=(18,10))

# Plot recency distribution
plt.subplot(3, 1, 1); sns.distplot(data_norm['Recency'])

# Plot frequency distribution
plt.subplot(3, 1, 2); sns.distplot(data_norm['Frequency'])

# Plot monetary value distribution
plt.subplot(3, 1, 3); sns.distplot(data_norm['MonetaryValue'])

# Show the plot
plt.show()

"""### `Skewness has been removed`

## Clustering with K-means Algorithm

We will build multiple clusters upon our RFM data and will try to find out the optimal number of clusters in our data using the `elbow method`.
"""

from sklearn.cluster import KMeans

sse=[]

#Fit KMeans and calculate sse for every k
for i in range(1,25,1):
    model=KMeans(n_clusters=i,random_state=40)
    model.fit(data_norm)
    sse.append(model.inertia_)

plt.figure(figsize=(10,4))

plt.title('The Elbow Method')
plt.xlabel('n_clusters');
plt.ylabel('Sum of squared errors')
plt.plot(range(1,25,1),sse,marker='o',markerfacecolor='r')
plt.xticks(ticks=range(0,25,1))
plt.grid()
plt.show()

"""From the above plot, we can see that the optimal number of cluster is 3 or 4 or 5.

Let us take k = 3 first.
"""

kmeans = KMeans(n_clusters=3, random_state=1)
kmeans.fit(data_norm)
cluster_labels = kmeans.labels_

data_norm_k3 = data_norm.assign(Cluster = cluster_labels) #Normalized RFM DATA
data_k3 = rfm_data.assign(Cluster = cluster_labels)#Orignal RFM Data

# Calculate average RFM values and size for each cluster like we did before
summary_k3 = data_k3.groupby(['Cluster']).agg({'Recency': 'mean',
                                                    'Frequency': 'mean',
                                                    'MonetaryValue': ['mean', 'count'],}).round(0)

summary_k3

"""Let us now take k = 4."""

kmeans = KMeans(n_clusters=4, random_state=1)
kmeans.fit(data_norm)
cluster_labels = kmeans.labels_

data_norm_k4 = data_norm.assign(Cluster = cluster_labels) #Normalized RFM DATA
data_k4 = rfm_data.assign(Cluster = cluster_labels)#Orignal RFM Data

# Calculate average RFM values and size for each cluster like we did before
summary_k4 = data_k4.groupby(['Cluster']).agg({'Recency': 'mean',
                                                    'Frequency': 'mean',
                                                    'MonetaryValue': ['mean', 'count'],}).round(0)

summary_k4

"""K=5"""

kmeans = KMeans(n_clusters=5, random_state=1)
kmeans.fit(data_norm)
cluster_labels = kmeans.labels_

data_norm_k5= data_norm.assign(Cluster = cluster_labels) #Normalized RFM DATA
data_k5= rfm_data.assign(Cluster = cluster_labels)#Orignal RFM Data

# Calculate average RFM values and size for each cluster like we did before
summary_k5 = data_k5.groupby(['Cluster']).agg({'Recency': 'mean',
                                                    'Frequency': 'mean',
                                                    'MonetaryValue': ['mean', 'count'],}).round(0)

summary_k5

"""## Profiling and Interpreting segments"""

display(summary_k3)
display(summary_k4)
display(summary_k5)

"""We can also build snakeplots to understand and compare the segments. Let us build a snakeplot for our data with all clusters value.

Before building snakeplots, let us assign back customerID values to the row indices.
"""

data_norm_k3.index = data['CustomerID'].astype(int)
data_norm_k4.index = data['CustomerID'].astype(int)
data_norm_k5.index = data['CustomerID'].astype(int)

data_norm_k4.head()

# Melt the data into along format so RFM values and metric names are stored in 1 column each like Feature and its value
data_melt_k3=pd.melt(data_norm_k3.reset_index(),id_vars=['CustomerID','Cluster'],
                  value_vars=['Recency','Frequency','MonetaryValue'],
                  var_name='Features',
                  value_name='Value')

data_melt_k4=pd.melt(data_norm_k4.reset_index(),id_vars=['CustomerID','Cluster'],
                  value_vars=['Recency','Frequency','MonetaryValue'],
                  var_name='Features',
                  value_name='Value')

data_melt_k5=pd.melt(data_norm_k5.reset_index(),id_vars=['CustomerID','Cluster'],
                  value_vars=['Recency','Frequency','MonetaryValue'],
                  var_name='Features',
                  value_name='Value')

data_melt_k4.head()

plt.figure(figsize=[18,4])
plt.title('Snake plot of standardized variables')
plt.subplot(1,3,1);sns.lineplot(x="Features", y="Value", hue='Cluster', data=data_melt_k3);plt.title('K=3')
plt.subplot(1,3,2);sns.lineplot(x="Features", y="Value", hue='Cluster', data=data_melt_k4);plt.title('K=4')
plt.subplot(1,3,3);sns.lineplot(x="Features", y="Value", hue='Cluster', data=data_melt_k5);plt.title('K=5')
plt.show()

"""From the above snake plot, we can see the distribution of recency, frequency, and monetary metric values across the clusters. The clusters seem to be separate from each other, which indicates a good heterogeneous mix of clusters. Best happens for k=3

#### `Assigning CustomerID index to data_k4 dataframe and rfm_data dataframe:`
"""

data_k4.index = data['CustomerID'].astype(int)
data_k4.head()

rfm_data.index = data['CustomerID'].astype(int)
rfm_data.head()

cluster_avg = data_k4.groupby(['Cluster']).mean()
population_avg = rfm_data.head().mean()

display(cluster_avg)
display(population_avg)

relative_imp = cluster_avg.divide(population_avg,axis=1)
relative_imp.round(2)

# Plot heatmap
plt.figure(figsize=(8, 4))
plt.title('Relative importance of Attributes')
sns.heatmap(data=relative_imp, annot=True, fmt='.2f', cmap='RdYlGn')
plt.show()

"""# Final Thoughts

From the above analysis, we can see that there should be 4 clusters in our data. To understand what these 4 clusters mean in a business scenario, we should look back the table comparing the clustering performance of 3 and 4 clusters for the mean values of recency, frequency, and monetary metric. On this basis, let us label the clusters as ‘New customers’, ‘Lost customers’, ‘Best customers’, and ‘At risk customers’.

Below is the table giving the RFM interpretation of each segment and the points that a company is recommended to keep in mind while designing the marketing strategy for that segment of customers.

![image](https://user-images.githubusercontent.com/86877457/132398189-8d90880b-6966-480b-944d-a9ffba803595.png)
"""


import grpc
import math
import time
import numpy as np
import robot_con.xarm_shuidi.xarm_shuidi_pb2 as aa_msg
import robot_con.xarm_shuidi.xarm_shuidi_pb2_grpc as aa_rpc
import motion.trajectory.piecewisepoly_scl as pwp

class XArmShuidiClient(object):

    def __init__(self, host="localhost:18300"):
        channel = grpc.insecure_channel(host)
        self.stub = aa_rpc.XArmShuidiStub(channel)

    def get_jnt_values(self, component_name="arm"):
        if component_name == "arm":
            return self.arm_get_jnt_values()

    def move_jnts(self, component_name, jnt_values, method='linear', max_jntspeed=math.pi):
        """
        TODO: use xarm function to get faster
        author: weiwei
        date: 20210729
        """
        if component_name == "arm":
            current_jnt_values = self.arm_get_jnt_values()
            print(current_jnt_values, jnt_values)
            if np.allclose(jnt_values, current_jnt_values, atol=1e-5):
                print("The robot's configuration is the same as the given one!")
                return
            self.arm_move_jspace_path(path=[self.arm_get_jnt_values(), jnt_values], method=method,
                                      max_jntspeed=max_jntspeed)

    def arm_get_jnt_values(self):
        jntvalues_msg = self.stub.arm_get_jnt_values(aa_msg.Empty())
        jnt_values = np.frombuffer(jntvalues_msg.data, dtype=np.float64)
        return jnt_values

    def arm_move_jspace_path(self,
                             path,
                             max_jntspeed=math.pi,
                             method='linear',
                             start_frame_id=1,
                             toggle_debug=False):
        """
        TODO: make speed even
        :param path: [jnt_values0, jnt_values1, ...], results of motion planning
        :return:
        author: weiwei
        date: 20190417
        """
        if not path or path is None:
            raise ValueError("The given is incorrect!")
        control_frequency = .005
        tpply = pwp.PiecewisePoly(method=method)
        interpolated_path, interpolated_spd, interpolated_acc, interpolated_x = \
            tpply.interpolate(path=path, control_frequency=.005, time_intervals=.1)
            # tpply.interpolate_by_max_jntspeed(path=path,
            #                                   control_frequency=control_frequency,
            #                                   max_jntspeed=max_jntspeed)
        if toggle_debug:
            import matplotlib.pyplot as plt
            # plt.plot(interplated_path)
            plt.subplot(311)
            for i in range(len(path)):
                plt.axvline(x=i)
            plt.plot(interpolated_path)
            plt.subplot(312)
            for i in range(len(path)):
                plt.axvline(x=i)
            plt.plot(interpolated_spd)
            plt.subplot(313)
            for i in range(len(path)):
                plt.axvline(x=i)
            plt.plot(interpolated_acc)
            plt.show()
            import pickle
            pickle.dump([interpolated_path, interpolated_spd, interpolated_acc], open("interpolated_traj.pkl", "wb"))
        interpolated_path = interpolated_path[start_frame_id:]
        path_msg = aa_msg.Path(length=len(interpolated_path),
                               njnts=len(interpolated_path[0]),
                               data=np.array(interpolated_path).tobytes())
        return_value = self.stub.arm_move_jspace_path(path_msg)
        if return_value == aa_msg.Status.ERROR:
            print("Something went wrong with the server!! Try again!")
            raise Exception()
        else:
            print("The rbt_s has finished the given motion.")

    def arm_get_jawwidth(self):
        gripper_msg = self.stub.arm_get_gripper_status(aa_msg.Empty())
        return (gripper_msg.position + 10) / 860 * .085

    def arm_jaw_to(self, jawwidth, speed=None):
        """
        both values are in percentage
        :param jawwidth: 0~100
        :param speed: 0~100
        :return:
        """
        if speed is None:
            speed = 5000
        else:
            speed = math.floor(5000 * speed / 100)
        position = math.floor(860 * jawwidth / 100) - 10
        gripper_msg = aa_msg.GripperStatus(speed=speed,
                                           position=position)
        return_value = self.stub.arm_jaw_to(gripper_msg)
        if return_value == aa_msg.Status.ERROR:
            print("Something went wrong with the server!! Try again!")
            raise Exception()
        else:
            print("The gripper has finished the given action.")

    def agv_move(self, linear_speed=.0, angular_speed=.0, time_interval=.5):
        while time_interval > 0:
            speed_msg = aa_msg.Speed(linear_velocity=linear_speed,
                                     angular_velocity=angular_speed)
            # try:
            return_value = self.stub.agv_move(speed_msg)
            if return_value == aa_msg.Status.ERROR:
                print("Something went wrong with the server!!")
                continue
            time_interval = time_interval - .5
            time.sleep(.3)
            # except Exception:
            #     pass

if __name__ == "__main__":
    import keyboard
    import basis.robot_math as rm
    import visualization.panda.world as wd
    import robot_sim.robots.xarm7_shuidi_mobile.xarm7_shuidi_mobile as rbt

    base = wd.World(cam_pos=[3, 1, 1.5], lookat_pos=[0, 0, 0.7])
    rbt_s = rbt.XArm7YunjiMobile()
    rbt_x = XArmShuidiClient(host="10.2.0.201:18300")
    jnt_values = rbt_x.arm_get_jnt_vlaues()
    jawwidth = rbt_x.arm_get_jawwidth()
    rbt_s.fk(jnt_values=jnt_values)
    rbt_s.jaw_to(jawwidth=jawwidth)
    rbt_s.gen_meshmodel().attach_to(base)
    # base.run()
    # rbt_x.agv_move(agv_linear_speed=-.1, agv_angular_speed=.1, time_intervals=5)
    agv_linear_speed = .2
    agv_angular_speed = .5
    arm_linear_speed = .02
    arm_angular_speed = .05
    while True:
        pressed_keys = {"w": keyboard.is_pressed('w'),
                        "a": keyboard.is_pressed('a'),
                        "s": keyboard.is_pressed('s'),
                        "d": keyboard.is_pressed('d'),
                        "r": keyboard.is_pressed('r'),  # x+ global
                        "t": keyboard.is_pressed('t'),  # x- global
                        "f": keyboard.is_pressed('f'),  # y+ global
                        "g": keyboard.is_pressed('g'),  # y- global
                        "v": keyboard.is_pressed('v'),  # z+ global
                        "b": keyboard.is_pressed('b'),  # z- global
                        "y": keyboard.is_pressed('y'),  # r+ global
                        "u": keyboard.is_pressed('u'),  # r- global
                        "h": keyboard.is_pressed('h'),  # p+ global
                        "j": keyboard.is_pressed('j'),  # p- global
                        "n": keyboard.is_pressed('n'),  # yaw+ global
                        "m": keyboard.is_pressed('m')}  # yaw- global
        # "R": keyboard.is_pressed('R'),  # x+ local
        # "T": keyboard.is_pressed('T'),  # x- local
        # "F": keyboard.is_pressed('F'),  # y+ local
        # "G": keyboard.is_pressed('G'),  # y- local
        # "V": keyboard.is_pressed('V'),  # z+ local
        # "B": keyboard.is_pressed('B'),  # z- local
        # "Y": keyboard.is_pressed('Y'),  # r+ local
        # "U": keyboard.is_pressed('U'),  # r- local
        # "H": keyboard.is_pressed('H'),  # p+ local
        # "J": keyboard.is_pressed('J'),  # p- local
        # "N": keyboard.is_pressed('N'),  # yaw+ local
        # "M": keyboard.is_pressed('M')}  # yaw- local
        values_list = list(pressed_keys.values())
        if pressed_keys["w"] and pressed_keys["a"]:
            rbt_x.agv_move(linear_speed=agv_linear_speed, angular_speed=agv_angular_speed, time_interval=.5)
        elif pressed_keys["w"] and pressed_keys["d"]:
            rbt_x.agv_move(linear_speed=agv_linear_speed, angular_speed=-agv_angular_speed, time_interval=.5)
        elif pressed_keys["s"] and pressed_keys["a"]:
            rbt_x.agv_move(linear_speed=-agv_linear_speed, angular_speed=-agv_angular_speed, time_interval=.5)
        elif pressed_keys["s"] and pressed_keys["d"]:
            rbt_x.agv_move(linear_speed=-agv_linear_speed, angular_speed=agv_angular_speed, time_interval=.5)
        elif pressed_keys["w"] and sum(values_list) == 1:  # if key 'q' is pressed
            rbt_x.agv_move(linear_speed=agv_linear_speed, angular_speed=.0, time_interval=.5)
        elif pressed_keys["s"] and sum(values_list) == 1:  # if key 'q' is pressed
            rbt_x.agv_move(linear_speed=-agv_linear_speed, angular_speed=.0, time_interval=.5)
        elif pressed_keys["a"] and sum(values_list) == 1:  # if key 'q' is pressed
            rbt_x.agv_move(linear_speed=.0, angular_speed=agv_angular_speed, time_interval=.5)
        elif pressed_keys["d"] and sum(values_list) == 1:  # if key 'q' is pressed
            rbt_x.agv_move(linear_speed=.0, angular_speed=-agv_angular_speed, time_interval=.5)
        elif any(pressed_keys[item] for item in ['r', 't', 'f', 'g', 'v', 'b', 'y', 'u', 'h', 'j', 'n', 'm']) and \
                sum(values_list) == 1:  # global
            tic = time.time()
            current_arm_tcp_pos, current_arm_tcp_rotmat = rbt_s.get_gl_tcp()
            rel_pos = np.zeros(3)
            rel_rotmat = np.eye(3)
            if pressed_keys['r']:
                rel_pos = np.array([arm_linear_speed * .5, 0, 0])
            elif pressed_keys['t']:
                rel_pos = np.array([-arm_linear_speed * .5, 0, 0])
            elif pressed_keys['f']:
                rel_pos = np.array([0, arm_linear_speed * .5, 0])
            elif pressed_keys['g']:
                rel_pos = np.array([0, -arm_linear_speed * .5, 0])
            elif pressed_keys['v']:
                rel_pos = np.array([0, 0, arm_linear_speed * .5])
            elif pressed_keys['b']:
                rel_pos = np.array([0, 0, -arm_linear_speed * .5])
            elif pressed_keys['y']:
                rel_rotmat = rm.rotmat_from_euler(arm_angular_speed * .5, 0, 0)
            elif pressed_keys['u']:
                rel_rotmat = rm.rotmat_from_euler(-arm_angular_speed * .5, 0, 0)
            elif pressed_keys['h']:
                rel_rotmat = rm.rotmat_from_euler(0, arm_angular_speed * .5, 0)
            elif pressed_keys['j']:
                rel_rotmat = rm.rotmat_from_euler(0, -arm_angular_speed * .5, 0)
            elif pressed_keys['n']:
                rel_rotmat = rm.rotmat_from_euler(0, 0, arm_angular_speed * .5)
            elif pressed_keys['m']:
                rel_rotmat = rm.rotmat_from_euler(0, 0, -arm_angular_speed * .5)
            new_arm_tcp_pos = current_arm_tcp_pos + rel_pos
            new_arm_tcp_rotmat = rel_rotmat.dot(current_arm_tcp_rotmat)
            last_jnt_values = rbt_s.get_jnt_values()
            new_jnt_values = rbt_s.ik(tgt_pos=new_arm_tcp_pos, tgt_rotmat=new_arm_tcp_rotmat)
            rbt_s.fk(jnt_values=new_jnt_values)
            toc = time.time()
            start_frame_id = math.ceil((toc - tic) / .01)
            rbt_x.arm_move_jspace_path([last_jnt_values, new_jnt_values], time_interval=.1,
                                       start_frame_id=start_frame_id)
        # elif any(pressed_keys[item] for item in ['R', 'T', 'F', 'G', 'V', 'B', 'Y', 'U', 'H', 'J', 'N', 'M']) and\
        #         sum(values_list) == 1: # local
        #     tic = time.time()
        #     rel_pos = np.zeros(3)
        #     rel_rotmat = np.eye(3)
        #     if pressed_keys['r']:
        #         rel_pos = np.array([arm_linear_speed * .5, 0, 0])
        #     elif pressed_keys['t']:
        #         rel_pos = np.array([-arm_linear_speed * .5, 0, 0])
        #     elif pressed_keys['f']:
        #         rel_pos = np.array([0, arm_linear_speed * .5, 0])
        #     elif pressed_keys['g']:
        #         rel_pos = np.array([0, -arm_linear_speed * .5, 0])
        #     elif pressed_keys['v']:
        #         rel_pos = np.array([0, 0, arm_linear_speed * .5])
        #     elif pressed_keys['b']:
        #         rel_pos = np.array([0, 0, -arm_linear_speed * .5])
        #     elif pressed_keys['y']:
        #         rel_rotmat = rm.rotmat_from_euler(arm_angular_speed*.5, 0, 0)
        #     elif pressed_keys['u']:
        #         rel_rotmat = rm.rotmat_from_euler(-arm_angular_speed*.5, 0, 0)
        #     elif pressed_keys['h']:
        #         rel_rotmat = rm.rotmat_from_euler(0, arm_angular_speed*.5, 0)
        #     elif pressed_keys['j']:
        #         rel_rotmat = rm.rotmat_from_euler(0, -arm_angular_speed * .5, 0)
        #     elif pressed_keys['n']:
        #         rel_rotmat = rm.rotmat_from_euler(0, 0, arm_angular_speed*.5)
        #     elif pressed_keys['m']:
        #         rel_rotmat = rm.rotmat_from_euler(0, 0, -arm_angular_speed*.5)
        #     new_arm_tcp_pos, new_arm_tcp_rotmat = rbt_s.cvt_loc_tcp_to_gl("arm",
        #                                                                   rel_obj_pos=rel_pos,
        #                                                                   rel_obj_rotmat=rel_rotmat)
        #     last_jnt_values = rbt_s.get_jnt_values()
        #     new_jnt_values = rbt_s.ik(tgt_pos=new_arm_tcp_pos, tgt_rotmat=new_arm_tcp_rotmat)
        #     rbt_s.fk(jnt_values=new_jnt_values)
        #     toc = time.time()
        #     start_frame_id = math.ceil((toc - tic) / .01)
        #     rbt_x.arm_move_jspace_path([last_jnt_values, new_jnt_values], time_intervals=.1, start_frame_id=start_frame_id)

# path = [[0, 0, 0, 0, 0, 0, 0]]wwwwwwwwwwww
# rbt_x.move_jspace_path(path)
# nxt.playPattern([anglesrad], [5.0])
# nxt.goOffPose()
# init_jnt_angles = rbt_x.get_jnt_vlaues()
# print(init_jnt_angles)
# init_jawwidth = rbt_x.get_jawwidth()
# print(init_jawwidth)
# rbt_x.jaw_to(0)


def selection_14():

    # Library import
    import numpy
    import matplotlib
    import matplotlib.pyplot   as plt
    import matplotlib.gridspec as gridspec

    # Library version
    matplotlib_version = matplotlib.__version__
    numpy_version      = numpy.__version__

    # Histo binning
    xBinning = numpy.linspace(0.0,1000.0,201,endpoint=True)

    # Creating data sequence: middle of each bin
    xData = numpy.array([2.5,7.5,12.5,17.5,22.5,27.5,32.5,37.5,42.5,47.5,52.5,57.5,62.5,67.5,72.5,77.5,82.5,87.5,92.5,97.5,102.5,107.5,112.5,117.5,122.5,127.5,132.5,137.5,142.5,147.5,152.5,157.5,162.5,167.5,172.5,177.5,182.5,187.5,192.5,197.5,202.5,207.5,212.5,217.5,222.5,227.5,232.5,237.5,242.5,247.5,252.5,257.5,262.5,267.5,272.5,277.5,282.5,287.5,292.5,297.5,302.5,307.5,312.5,317.5,322.5,327.5,332.5,337.5,342.5,347.5,352.5,357.5,362.5,367.5,372.5,377.5,382.5,387.5,392.5,397.5,402.5,407.5,412.5,417.5,422.5,427.5,432.5,437.5,442.5,447.5,452.5,457.5,462.5,467.5,472.5,477.5,482.5,487.5,492.5,497.5,502.5,507.5,512.5,517.5,522.5,527.5,532.5,537.5,542.5,547.5,552.5,557.5,562.5,567.5,572.5,577.5,582.5,587.5,592.5,597.5,602.5,607.5,612.5,617.5,622.5,627.5,632.5,637.5,642.5,647.5,652.5,657.5,662.5,667.5,672.5,677.5,682.5,687.5,692.5,697.5,702.5,707.5,712.5,717.5,722.5,727.5,732.5,737.5,742.5,747.5,752.5,757.5,762.5,767.5,772.5,777.5,782.5,787.5,792.5,797.5,802.5,807.5,812.5,817.5,822.5,827.5,832.5,837.5,842.5,847.5,852.5,857.5,862.5,867.5,872.5,877.5,882.5,887.5,892.5,897.5,902.5,907.5,912.5,917.5,922.5,927.5,932.5,937.5,942.5,947.5,952.5,957.5,962.5,967.5,972.5,977.5,982.5,987.5,992.5,997.5])

    # Creating weights for histo: y15_MET_0
    y15_MET_0_weights = numpy.array([405.281563328,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y15_MET_1
    y15_MET_1_weights = numpy.array([102.864642984,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y15_MET_2
    y15_MET_2_weights = numpy.array([477.807617489,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y15_MET_3
    y15_MET_3_weights = numpy.array([573.693281578,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y15_MET_4
    y15_MET_4_weights = numpy.array([136.765512365,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y15_MET_5
    y15_MET_5_weights = numpy.array([23.8424547514,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y15_MET_6
    y15_MET_6_weights = numpy.array([6.03761405013,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y15_MET_7
    y15_MET_7_weights = numpy.array([0.336661363917,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y15_MET_8
    y15_MET_8_weights = numpy.array([0.0252024530109,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y15_MET_9
    y15_MET_9_weights = numpy.array([117.296273077,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y15_MET_10
    y15_MET_10_weights = numpy.array([496.072960385,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y15_MET_11
    y15_MET_11_weights = numpy.array([814.235720577,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y15_MET_12
    y15_MET_12_weights = numpy.array([292.998877962,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y15_MET_13
    y15_MET_13_weights = numpy.array([44.5623415996,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y15_MET_14
    y15_MET_14_weights = numpy.array([10.8894797712,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y15_MET_15
    y15_MET_15_weights = numpy.array([0.674793936787,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y15_MET_16
    y15_MET_16_weights = numpy.array([0.0436957899231,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating a new Canvas
    fig   = plt.figure(figsize=(12,6),dpi=80)
    frame = gridspec.GridSpec(1,1,right=0.7)
    pad   = fig.add_subplot(frame[0])

    # Creating a new Stack
    pad.hist(x=xData, bins=xBinning, weights=y15_MET_0_weights+y15_MET_1_weights+y15_MET_2_weights+y15_MET_3_weights+y15_MET_4_weights+y15_MET_5_weights+y15_MET_6_weights+y15_MET_7_weights+y15_MET_8_weights+y15_MET_9_weights+y15_MET_10_weights+y15_MET_11_weights+y15_MET_12_weights+y15_MET_13_weights+y15_MET_14_weights+y15_MET_15_weights+y15_MET_16_weights,\
             label="$bg\_dip\_1600\_inf$", histtype="step", rwidth=1.0,\
             color=None, edgecolor="#e5e5e5", linewidth=4, linestyle="dashdot",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y15_MET_0_weights+y15_MET_1_weights+y15_MET_2_weights+y15_MET_3_weights+y15_MET_4_weights+y15_MET_5_weights+y15_MET_6_weights+y15_MET_7_weights+y15_MET_8_weights+y15_MET_9_weights+y15_MET_10_weights+y15_MET_11_weights+y15_MET_12_weights+y15_MET_13_weights+y15_MET_14_weights+y15_MET_15_weights,\
             label="$bg\_dip\_1200\_1600$", histtype="step", rwidth=1.0,\
             color=None, edgecolor="#f2f2f2", linewidth=4, linestyle="dashdot",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y15_MET_0_weights+y15_MET_1_weights+y15_MET_2_weights+y15_MET_3_weights+y15_MET_4_weights+y15_MET_5_weights+y15_MET_6_weights+y15_MET_7_weights+y15_MET_8_weights+y15_MET_9_weights+y15_MET_10_weights+y15_MET_11_weights+y15_MET_12_weights+y15_MET_13_weights+y15_MET_14_weights,\
             label="$bg\_dip\_800\_1200$", histtype="step", rwidth=1.0,\
             color=None, edgecolor="#ccc6aa", linewidth=4, linestyle="dashdot",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y15_MET_0_weights+y15_MET_1_weights+y15_MET_2_weights+y15_MET_3_weights+y15_MET_4_weights+y15_MET_5_weights+y15_MET_6_weights+y15_MET_7_weights+y15_MET_8_weights+y15_MET_9_weights+y15_MET_10_weights+y15_MET_11_weights+y15_MET_12_weights+y15_MET_13_weights,\
             label="$bg\_dip\_600\_800$", histtype="step", rwidth=1.0,\
             color=None, edgecolor="#ccc6aa", linewidth=4, linestyle="dashdot",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y15_MET_0_weights+y15_MET_1_weights+y15_MET_2_weights+y15_MET_3_weights+y15_MET_4_weights+y15_MET_5_weights+y15_MET_6_weights+y15_MET_7_weights+y15_MET_8_weights+y15_MET_9_weights+y15_MET_10_weights+y15_MET_11_weights+y15_MET_12_weights,\
             label="$bg\_dip\_400\_600$", histtype="step", rwidth=1.0,\
             color=None, edgecolor="#c1bfa8", linewidth=4, linestyle="dashdot",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y15_MET_0_weights+y15_MET_1_weights+y15_MET_2_weights+y15_MET_3_weights+y15_MET_4_weights+y15_MET_5_weights+y15_MET_6_weights+y15_MET_7_weights+y15_MET_8_weights+y15_MET_9_weights+y15_MET_10_weights+y15_MET_11_weights,\
             label="$bg\_dip\_200\_400$", histtype="step", rwidth=1.0,\
             color=None, edgecolor="#bab5a3", linewidth=4, linestyle="dashdot",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y15_MET_0_weights+y15_MET_1_weights+y15_MET_2_weights+y15_MET_3_weights+y15_MET_4_weights+y15_MET_5_weights+y15_MET_6_weights+y15_MET_7_weights+y15_MET_8_weights+y15_MET_9_weights+y15_MET_10_weights,\
             label="$bg\_dip\_100\_200$", histtype="step", rwidth=1.0,\
             color=None, edgecolor="#b2a596", linewidth=4, linestyle="dashdot",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y15_MET_0_weights+y15_MET_1_weights+y15_MET_2_weights+y15_MET_3_weights+y15_MET_4_weights+y15_MET_5_weights+y15_MET_6_weights+y15_MET_7_weights+y15_MET_8_weights+y15_MET_9_weights,\
             label="$bg\_dip\_0\_100$", histtype="step", rwidth=1.0,\
             color=None, edgecolor="#b7a39b", linewidth=4, linestyle="dashdot",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y15_MET_0_weights+y15_MET_1_weights+y15_MET_2_weights+y15_MET_3_weights+y15_MET_4_weights+y15_MET_5_weights+y15_MET_6_weights+y15_MET_7_weights+y15_MET_8_weights,\
             label="$bg\_vbf\_1600\_inf$", histtype="step", rwidth=1.0,\
             color=None, edgecolor="#ad998c", linewidth=4, linestyle="dashdot",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y15_MET_0_weights+y15_MET_1_weights+y15_MET_2_weights+y15_MET_3_weights+y15_MET_4_weights+y15_MET_5_weights+y15_MET_6_weights+y15_MET_7_weights,\
             label="$bg\_vbf\_1200\_1600$", histtype="step", rwidth=1.0,\
             color=None, edgecolor="#9b8e82", linewidth=4, linestyle="dashdot",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y15_MET_0_weights+y15_MET_1_weights+y15_MET_2_weights+y15_MET_3_weights+y15_MET_4_weights+y15_MET_5_weights+y15_MET_6_weights,\
             label="$bg\_vbf\_800\_1200$", histtype="step", rwidth=1.0,\
             color=None, edgecolor="#876656", linewidth=4, linestyle="dashdot",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y15_MET_0_weights+y15_MET_1_weights+y15_MET_2_weights+y15_MET_3_weights+y15_MET_4_weights+y15_MET_5_weights,\
             label="$bg\_vbf\_600\_800$", histtype="step", rwidth=1.0,\
             color=None, edgecolor="#afcec6", linewidth=4, linestyle="dashdot",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y15_MET_0_weights+y15_MET_1_weights+y15_MET_2_weights+y15_MET_3_weights+y15_MET_4_weights,\
             label="$bg\_vbf\_400\_600$", histtype="step", rwidth=1.0,\
             color=None, edgecolor="#84c1a3", linewidth=4, linestyle="dashdot",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y15_MET_0_weights+y15_MET_1_weights+y15_MET_2_weights+y15_MET_3_weights,\
             label="$bg\_vbf\_200\_400$", histtype="step", rwidth=1.0,\
             color=None, edgecolor="#89a8a0", linewidth=4, linestyle="dashdot",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y15_MET_0_weights+y15_MET_1_weights+y15_MET_2_weights,\
             label="$bg\_vbf\_100\_200$", histtype="step", rwidth=1.0,\
             color=None, edgecolor="#829e8c", linewidth=4, linestyle="dashdot",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y15_MET_0_weights+y15_MET_1_weights,\
             label="$bg\_vbf\_0\_100$", histtype="step", rwidth=1.0,\
             color=None, edgecolor="#adbcc6", linewidth=4, linestyle="dashdot",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y15_MET_0_weights,\
             label="$signal$", histtype="step", rwidth=1.0,\
             color=None, edgecolor="#7a8e99", linewidth=3, linestyle="dashed",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    # Axis
    plt.rc('text',usetex=False)
    plt.xlabel(r"TET",\
               fontsize=16,color="black")
    plt.ylabel(r"$\mathrm{Events}$ $(\mathcal{L}_{\mathrm{int}} = 40.0\ \mathrm{fb}^{-1})$ ",\
               fontsize=16,color="black")

    # Boundary of y-axis
    ymax=(y15_MET_0_weights+y15_MET_1_weights+y15_MET_2_weights+y15_MET_3_weights+y15_MET_4_weights+y15_MET_5_weights+y15_MET_6_weights+y15_MET_7_weights+y15_MET_8_weights+y15_MET_9_weights+y15_MET_10_weights+y15_MET_11_weights+y15_MET_12_weights+y15_MET_13_weights+y15_MET_14_weights+y15_MET_15_weights+y15_MET_16_weights).max()*1.1
    #ymin=0 # linear scale
    ymin=min([x for x in (y15_MET_0_weights+y15_MET_1_weights+y15_MET_2_weights+y15_MET_3_weights+y15_MET_4_weights+y15_MET_5_weights+y15_MET_6_weights+y15_MET_7_weights+y15_MET_8_weights+y15_MET_9_weights+y15_MET_10_weights+y15_MET_11_weights+y15_MET_12_weights+y15_MET_13_weights+y15_MET_14_weights+y15_MET_15_weights+y15_MET_16_weights) if x])/100. # log scale
    plt.gca().set_ylim(ymin,ymax)

    # Log/Linear scale for X-axis
    plt.gca().set_xscale("linear")
    #plt.gca().set_xscale("log",nonposx="clip")

    # Log/Linear scale for Y-axis
    #plt.gca().set_yscale("linear")
    plt.gca().set_yscale("log",nonposy="clip")

    # Legend
    plt.legend(bbox_to_anchor=(1.05,1), loc=2, borderaxespad=0.)

    # Saving the image
    plt.savefig('../../HTML/MadAnalysis5job_0/selection_14.png')
    plt.savefig('../../PDF/MadAnalysis5job_0/selection_14.png')
    plt.savefig('../../DVI/MadAnalysis5job_0/selection_14.eps')

# Running!
if __name__ == '__main__':
    selection_14()