File size: 7,519 Bytes
1794e4f
c8a77b2
5810688
 
c8a77b2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3c5e633
 
c8a77b2
 
 
 
 
 
 
 
 
1794e4f
c8a77b2
1794e4f
 
c8a77b2
2187b60
3c5e633
2187b60
 
3c5e633
c8a77b2
 
 
 
2187b60
 
 
c8a77b2
 
f1bd5a3
2187b60
c8a77b2
2187b60
c8a77b2
 
5810688
3c5e633
 
 
 
 
 
 
 
 
 
 
 
 
5810688
 
 
 
 
 
 
c8a77b2
5810688
 
 
 
 
 
 
c8a77b2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2187b60
32a7408
2187b60
32a7408
 
 
2187b60
 
 
3c5e633
2187b60
 
 
 
3c5e633
2187b60
 
3c5e633
 
2187b60
3c5e633
 
 
 
 
 
 
 
 
2187b60
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
import streamlit.components.v1 as components
import streamlit as st
from random import randrange, uniform
import pandas as pd
import logging
import numpy as np

COL_NAMES = ['Time step',
 'Transaction type',
 'Amount transferred',
 'Sender\'s initial balance',
 'Sender\'s new balance',
 'Recipient\'s initial balance',
 'Recipient\'s new balance',
 "Sender exactly credited",
 "Receiver exactly credited",
 'Amount > 450 000',
 'Frequent receiver',
 'Merchant receiver',
 'Sender ID',
 'Receiver ID']

feature_texts = {0: "Time step: ", 1: "Amount transferred: ", 2: "Initial balance of sender: ", 3: "New balance of sender: ", 
                 4: "Initial balance of recipient: ", 5: "New balance of recipient: ", 6: "Sender's balance was exactly credited: ",
                   7: "Receiver's balance was exactly credited: ", 8: "Transaction over 450.000: ", 9: "Frequent receiver of transactions: ", 10: "Receiver is merchant: ", 11: "Sender ID: ", 12: "Receiver ID: ",
                   13: "Transaction type is Cash out", 14: "Transaction type is Transfer", 15: "Transaction type is Payment", 16: "Transaction type is Cash in", 17: "Transaction type is Debit"}

CATEGORIES = np.array(['CASH_OUT', 'TRANSFER', 'PAYMENT', 'CASH_IN', 'DEBIT']) 

def transformation(input, categories):
    new_x = input
    cat = np.array(input[1])
    del new_x[1]
    result_array = np.zeros(5, dtype=int)
    match_index = np.where(categories == cat)[0]
    result_array[match_index] = 1
    new_x.extend(result_array.tolist())
    python_objects = [np_type.item() if isinstance(np_type, np.generic) else np_type for np_type in new_x]
    return python_objects

def get_request_body(datapoint):
    data = datapoint.iloc[0].tolist()
    instances = [int(x) if isinstance(x, (np.int32, np.int64)) else x for x in data]
    request_body = {'instances': [instances]}
    return request_body


def get_explainability_texts(shap_values, feature_texts):
    # Separate positive and negative values, keep indice as corresponds to key
    positive_dict = {index: val for index, val in enumerate(shap_values) if val > 0}
    # Sort dictionaries based on the magnitude of values
    sorted_positive_indices = [index for index, _ in sorted(positive_dict.items(), key=lambda item: abs(item[1]), reverse=True)]
    positive_texts = [feature_texts[x] for x in sorted_positive_indices]
    positive_texts = positive_texts[2:]
    sorted_positive_indices = sorted_positive_indices[2:]
    if len(positive_texts) > 5:
        positive_texts = positive_texts[:5]
        sorted_positive_indices = sorted_positive_indices[:5]
    return positive_texts, sorted_positive_indices


def get_explainability_values(pos_indices, datapoint):
    data = datapoint.iloc[0].tolist()
    rounded_data = [round(value, 2) if isinstance(value, float) else value for value in data]
    transformed_data = transformation(input=rounded_data, categories=CATEGORIES)
    vals = []
    for idx in pos_indices:
        if idx in range(6,11) or idx in range(13,18):
            val = str(bool(transformed_data[idx])).capitalize()
        else:
            val = transformed_data[idx]
        vals.append(val)
    return vals

# def get_weights(shap_values, sorted_indices):
#     weights = [shap_values[x] for x in sorted_indices]
#     total_sum = sum(weights)
#     scaled_values = [val/total_sum for val in weights]
#     return scaled_values

def get_weights(shap_values, sorted_indices, target_sum=0.95):
    weights = [shap_values[x] for x in sorted_indices]
    total_sum = sum(weights)
    # Scale to the target sum (0.95 in this case)
    scaled_values = [val * (target_sum / total_sum) for val in weights]
    return scaled_values

def get_fake_certainty():
    # Generate a random certainty between 75% and 99%
    fake_certainty = uniform(0.75, 0.99)
    formatted_fake_certainty = "{:.2%}".format(fake_certainty)
    return formatted_fake_certainty


def get_random_suspicious_transaction(data):
    suspicious_data=data[data["isFraud"]==1]
    max_n=len(suspicious_data)
    random_nr=randrange(max_n)
    suspicous_transaction = suspicious_data[random_nr-1:random_nr].drop("isFraud", axis=1)
    return suspicous_transaction


def send_evaluation(client, deployment_id, request_log_id, prediction_log_id, evaluation_input):
    """Send evaluation to Deeploy."""
    try:
        with st.spinner("Submitting response..."):
            # Call the explain endpoint as it also includes the prediction
            client.evaluate(deployment_id, request_log_id, prediction_log_id, evaluation_input)
        return True
    except Exception as e:
        logging.error(e)
        st.error(
            "Failed to submit feedback."
            + "Check whether you are using the right model URL and Token. "
            + "Contact Deeploy if the problem persists."
        )
        st.write(f"Error message: {e}")


def get_model_url():
    """Get model url and retrieve workspace id and deployment id from it"""
    model_url = st.text_area(
        "Model URL (default is the demo deployment)",
        "https://api.app.deeploy.ml/workspaces/708b5808-27af-461a-8ee5-80add68384c7/deployments/ac56dbdf-ba04-462f-aa70-5a0d18698e42/",
        height=125,
    )
    elems = model_url.split("/")
    try:
        workspace_id = elems[4]
        deployment_id = elems[6]
    except IndexError:
        workspace_id = ""
        deployment_id = ""
    return model_url, workspace_id, deployment_id

def get_comment_explanation(certainty, explainability_texts, explainability_values):
    cleaned = [x.replace(':', '') for x in explainability_texts]
    fi = [f'{cleaned[i]} is {x}' for i, x in enumerate(explainability_values)]
    fi.insert(0, 'Important suspicious features: ')
    result = '\n'.join(fi)
    comment = f"Model certainty is {certainty}" + '\n''\n' + result
    return comment

def create_data_input_table(datapoint, col_names):
    st.subheader("Transaction details")
    data = datapoint.iloc[0].tolist()
    data[7:12] = [bool(value) for value in data[7:12]]
    rounded_list = [round(value, 2) if isinstance(value, float) else value for value in data]
    df = pd.DataFrame({"Feature name": col_names, "Value": rounded_list })
    st.dataframe(df, hide_index=True, use_container_width=True, height=35*len(df)+38) #width=450

# Create a function to generate a table
def create_table(texts, values, weights, title):
    df = pd.DataFrame({"Feature Explanation": texts, 'Value': values, 'Weight': weights})
    st.markdown(f'#### {title}')  # Markdown for styling
    st.dataframe(df, hide_index=True, use_container_width=True, column_config={
                     'Weight': st.column_config.ProgressColumn(
                         'Weight',
                         width='small',
                         format="%.2f",
                         min_value=0,
                         max_value=1
                     )
                 }) #width=450  # Display a simple table


def ChangeButtonColour(widget_label, font_color, background_color='transparent'):
    htmlstr = f"""
        <script>
            var elements = window.parent.document.querySelectorAll('button');
            for (var i = 0; i < elements.length; ++i) {{ 
                if (elements[i].innerText == '{widget_label}') {{ 
                    elements[i].style.color ='{font_color}';
                    elements[i].style.background = '{background_color}'
                }}
            }}
        </script>
        """
    components.html(f"{htmlstr}", height=0, width=0)