add recommender to repository

This commit is contained in:
hannes.kuchelmeister
2020-05-04 10:49:59 +02:00
parent 67ae83e0bd
commit 719f3d5ea7
51 changed files with 5792 additions and 0 deletions

133
.gitignore vendored Normal file
View File

@@ -0,0 +1,133 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
*.pdf
out/
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/

32
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,32 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python: app.py",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/src/app.py",
"console": "integratedTerminal",
"env": {"PYTHONPATH": "${workspaceRoot}/src/"}
},
{
"name": "Python: evaluation",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/eval.py",
"console": "integratedTerminal",
"env": {"PYTHONPATH": "${workspaceRoot}/"}
},
{
"name": "Python: visualization",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/vis.py",
"console": "integratedTerminal",
"env": {"PYTHONPATH": "${workspaceRoot}/"}
}
]
}

1
data_gen/gen.bat Normal file
View File

@@ -0,0 +1 @@
python gen_diagram.py

115
data_gen/gen_diagram.py Normal file
View File

@@ -0,0 +1,115 @@
import matplotlib.pyplot as plt
import pandas as pd
import os
def setAxLinesBW(ax):
"""
Take each Line2D in the axes, ax, and convert the line style to be
suitable for black and white viewing.
"""
MARKERSIZE = 3
COLORMAP = {
'#ff7f0e': {'marker': None, 'dash': [3,4]},
'#1f77b4': {'marker': None, 'dash': [1,1]},
'#2ca02c': {'marker': None, 'dash': (None,None)}
}
lines_to_adjust = ax.get_lines()
try:
lines_to_adjust += ax.get_legend().get_lines()
except AttributeError:
pass
for line in lines_to_adjust:
origColor = line.get_color()
line.set_color('black')
line.set_dashes(COLORMAP[origColor]['dash'])
line.set_marker(COLORMAP[origColor]['marker'])
line.set_markersize(MARKERSIZE)
def setFigLinesBW(fig):
"""
Take each axes in the figure, and for each line in the axes, make the
line viewable in black and white.
"""
for ax in fig.get_axes():
setAxLinesBW(ax)
def load_data_frame(path):
frame = pd.read_csv(path, index_col=0).T
frame.index = frame.index.astype(int)
return frame
def new_fig(subplot_row=1, subplot_column=2, dpi=300, title="Untitled"):
figure, axes = plt.subplots(subplot_row, subplot_column, sharey=True)
#figure.tight_layout(pad=1.5)
for axis in axes:
axis.tick_params(
axis="y", # both major and minor ticks are affected
left=True,
labelleft=True,
labelright=True)
figure.canvas.set_window_title(title)
figure.dpi = dpi
figure.set_figwidth(4 * subplot_column)
figure.set_figheight(4 * subplot_row)
return axes
happy_dictator = load_data_frame("./{}".format("happy_dictator.csv"))
unhappy_dictator = load_data_frame("./{}".format("unhappy_dictator.csv"))
axes = new_fig()
axes[0].set_title("satisfaction")
#axes[0].set_xlim(x_lim)
axes[0].set_xlabel("tc")
axes[0].set_ylabel("number of people")
happy_dictator.plot(ax=axes[0])
setAxLinesBW(axes[0])
axes[1].set_title("dissatisfaction")
axes[1].set_xlabel("tc")
axes[1].set_ylabel("number of people")
#axes[1].set_xlim(x_lim)
unhappy_dictator.plot(ax=axes[1])
setAxLinesBW(axes[1])
plt.savefig("./dictator.pdf",format="pdf")
happy_change = load_data_frame("./{}".format("happy_change.csv"))
unhappy_change = load_data_frame("./{}".format("unhappy_change.csv"))
axes = new_fig()
axes[0].set_title("satisfaction change")
#axes[0].set_xlim(x_lim)
axes[0].set_xlabel("tc")
axes[0].set_ylabel("number of people")
happy_change.plot(ax=axes[0])
setAxLinesBW(axes[0])
axes[1].set_title("dissatisfaction change")
axes[1].set_xlabel("tc")
axes[1].set_ylabel("number of people")
#axes[1].set_xlim(x_lim)
unhappy_change.plot(ax=axes[1])
setAxLinesBW(axes[1])
plt.savefig("./change.pdf",format="pdf")
plt.show()
plt.close()

View File

@@ -0,0 +1,4 @@
,10,20,30,40,50,60,63,65,67,70,73,75,77,80,81,82,83,84,85,87,90,91,92,93,94
heterogenous,0.079,0.166,0.256,0.232,0.375,0.631,0.625,0.605,0.561,0.501,0.427,0.351,0.255,0.153,0.083,0.062,-0.002,-0.048,-0.14,-0.282,-0.442,-0.568,-0.633,-0.802,-0.895
random,0.01,0.05,0.099,0.153,0.266,0.533,0.611,0.668,0.716,0.773,0.831,0.858,0.869,0.853,0.848,0.83,0.804,0.769,0.737,0.643,0.405,0.243,0.137,-0.155,-0.337
homogenous,0,0,0,0,0,0,0,0,0,0,0.004,0.005,0.006,0.011,0.011,0.014,0.026,0.031,0.048,0.078,0.164,0.21,0.267,0.303,0.271
1 10 20 30 40 50 60 63 65 67 70 73 75 77 80 81 82 83 84 85 87 90 91 92 93 94
2 heterogenous 0.079 0.166 0.256 0.232 0.375 0.631 0.625 0.605 0.561 0.501 0.427 0.351 0.255 0.153 0.083 0.062 -0.002 -0.048 -0.14 -0.282 -0.442 -0.568 -0.633 -0.802 -0.895
3 random 0.01 0.05 0.099 0.153 0.266 0.533 0.611 0.668 0.716 0.773 0.831 0.858 0.869 0.853 0.848 0.83 0.804 0.769 0.737 0.643 0.405 0.243 0.137 -0.155 -0.337
4 homogenous 0 0 0 0 0 0 0 0 0 0 0.004 0.005 0.006 0.011 0.011 0.014 0.026 0.031 0.048 0.078 0.164 0.21 0.267 0.303 0.271

View File

@@ -0,0 +1,4 @@
,10,20,30,40,50,60,63,65,67,70,73,75,77,80,81,82,83,84,85,87,90,91,92,93,94
heterogenous,3.7541875,3.805,3.654,3.594,3.343,2.807,2.683,2.61,2.535,2.387,2.263,2.192,2.136,2.014,1.948,1.916,1.837,1.814,1.746,1.638,1.448,1.352,1.312,1.235,1.186
random,3.989,3.945,3.878,3.8,3.655,3.262,3.138,3.041,2.938,2.781,2.618,2.51,2.404,2.229,2.141,2.102,2.024,1.992,1.88,1.727,1.513,1.396,1.324,1.183,1.121
homogenous,4,4,4,4,4,4,4,4,4,4,3.996,3.995,3.994,3.989,3.988,3.985,3.971,3.963,3.944,3.905,3.756,3.596,3.443,3.025,2.674
1 10 20 30 40 50 60 63 65 67 70 73 75 77 80 81 82 83 84 85 87 90 91 92 93 94
2 heterogenous 3.7541875 3.805 3.654 3.594 3.343 2.807 2.683 2.61 2.535 2.387 2.263 2.192 2.136 2.014 1.948 1.916 1.837 1.814 1.746 1.638 1.448 1.352 1.312 1.235 1.186
3 random 3.989 3.945 3.878 3.8 3.655 3.262 3.138 3.041 2.938 2.781 2.618 2.51 2.404 2.229 2.141 2.102 2.024 1.992 1.88 1.727 1.513 1.396 1.324 1.183 1.121
4 homogenous 4 4 4 4 4 4 4 4 4 4 3.996 3.995 3.994 3.989 3.988 3.985 3.971 3.963 3.944 3.905 3.756 3.596 3.443 3.025 2.674

View File

@@ -0,0 +1,4 @@
,10,20,30,40,50,60,63,65,67,70,73,75,77,80,81,82,83,84,85,87,90,91,92,93,94
heterogenous,0,-0.079,-0.166,-0.256,-0.232,-0.375,-0.417,-0.474,-0.589,-0.631,-0.625,-0.605,-0.561,-0.501,-0.501,-0.464,-0.427,-0.377,-0.351,-0.255,-0.153,-0.083,-0.062,0.002,0.048
random,0,-0.01,-0.05,-0.099,-0.153,-0.266,-0.336,-0.377,-0.441,-0.533,-0.611,-0.668,-0.716,-0.773,-0.783,-0.818,-0.831,-0.841,-0.858,-0.869,-0.853,-0.848,-0.83,-0.804,-0.769
homogenous,0,0,0,0,0,0,0,0,0,0,0,0,0,-0.002,-0.002,-0.003,-0.004,-0.004,-0.005,-0.006,-0.011,-0.011,-0.014,-0.026,-0.031
1 10 20 30 40 50 60 63 65 67 70 73 75 77 80 81 82 83 84 85 87 90 91 92 93 94
2 heterogenous 0 -0.079 -0.166 -0.256 -0.232 -0.375 -0.417 -0.474 -0.589 -0.631 -0.625 -0.605 -0.561 -0.501 -0.501 -0.464 -0.427 -0.377 -0.351 -0.255 -0.153 -0.083 -0.062 0.002 0.048
3 random 0 -0.01 -0.05 -0.099 -0.153 -0.266 -0.336 -0.377 -0.441 -0.533 -0.611 -0.668 -0.716 -0.773 -0.783 -0.818 -0.831 -0.841 -0.858 -0.869 -0.853 -0.848 -0.83 -0.804 -0.769
4 homogenous 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.002 -0.002 -0.003 -0.004 -0.004 -0.005 -0.006 -0.011 -0.011 -0.014 -0.026 -0.031

View File

@@ -0,0 +1,4 @@
,10,20,30,40,50,60,63,65,67,70,73,75,77,80,81,82,83,84,85,87,90,91,92,93,94
heterogenous,0,0.081,0.195,0.346,0.406,0.657,0.762,0.859,1.052,1.193,1.317,1.39,1.465,1.613,1.65,1.71,1.737,1.793,1.808,1.864,1.986,2.052,2.084,2.163,2.186
random,0,0.011,0.055,0.122,0.2,0.345,0.448,0.513,0.604,0.738,0.862,0.959,1.062,1.219,1.253,1.34,1.382,1.451,1.49,1.596,1.771,1.859,1.898,1.976,2.008
homogenous,0,0,0,0,0,0,0,0,0,0,0,0,0,0.002,0.002,0.003,0.004,0.004,0.005,0.006,0.011,0.012,0.015,0.029,0.037
1 10 20 30 40 50 60 63 65 67 70 73 75 77 80 81 82 83 84 85 87 90 91 92 93 94
2 heterogenous 0 0.081 0.195 0.346 0.406 0.657 0.762 0.859 1.052 1.193 1.317 1.39 1.465 1.613 1.65 1.71 1.737 1.793 1.808 1.864 1.986 2.052 2.084 2.163 2.186
3 random 0 0.011 0.055 0.122 0.2 0.345 0.448 0.513 0.604 0.738 0.862 0.959 1.062 1.219 1.253 1.34 1.382 1.451 1.49 1.596 1.771 1.859 1.898 1.976 2.008
4 homogenous 0 0 0 0 0 0 0 0 0 0 0 0 0 0.002 0.002 0.003 0.004 0.004 0.005 0.006 0.011 0.012 0.015 0.029 0.037

1
db.json Normal file

File diff suppressed because one or more lines are too long

426
eval.py Normal file
View File

@@ -0,0 +1,426 @@
import sys
import os
import datetime
import pandas as pd
import multiprocessing
import itertools
sys.path.append("./src")
sys.path.append("./evaluation")
from model.product_structure_model import ProductStructureModel
from model.preferences_model import Preferences
from model.configuration_model import ConfigurationModel
from managers.recommendation_manager import SimpleConfigurationMaxSelector
from scoring.scoring_functions import ReduceScoringFunctionFactory
from user_type_mappings import TYPE_ATHLETE, TYPE_CONSUMER, TYPE_ENVIRONMENTALIST, TYPE_OWNER, TYPE_RANDOM
import operator
import time
import numpy as np
import matplotlib.pyplot as pp
import random
import math
import json
with open('./evaluation/product_structure.json') as json_file:
data = json.load(json_file)
product_structure = ProductStructureModel(data)
from tinydb import TinyDB
def DB():
return TinyDB('eval.json')
def DB_CONFIG():
return DB().table('CONFIG')
def DB_PRODUCT_STRUCTURE():
return DB().table('PRODUCT_STRUCTURE')
CONFIGURATIONS_UNFINISHED = []
PREFERENCES_RANDOM_MEMBER = []
PREFERENCES_ALL = []
def generate_group_preferences(user_type_mappings, amount = 1000):
global PREFERENCES_RANDOM_MEMBER
global PREFERENCES_ALL
characteristics = product_structure.get_list_of_characteristics()
PREFERENCES_ALL = []
PREFERENCES_RANDOM_MEMBER = []
for i in range(amount):
users = []
single_user = []
counter = random.randint(0, len(user_type_mappings) - 1)
for mapping in user_type_mappings:
ratings = []
for char in characteristics:
value = mapping[char.elementId].generateNumber()
ratings.append({
"code": char.elementId,
"value": value,
})
user = {
"user": mapping['name'],
"ratings": ratings,
}
users.append(user)
if counter == 0:
single_user.append(user)
counter -= 1
PREFERENCES_ALL.append( Preferences({'preferences' : users}) )
PREFERENCES_RANDOM_MEMBER.append( Preferences({'preferences' : single_user}) )
return PREFERENCES_ALL
def generate_unfinished_configurations(fullness=0.3, amount=1000):
configurations = TinyDB('./evaluation/eval.json').table('CONFIG').all()
global CONFIGURATIONS_UNFINISHED
characteristics = list(map(lambda x: x.elementId,ProductStructureModel(data).get_list_of_characteristics()))
CONFIGURATIONS_UNFINISHED = []
for i in range(amount):
final_config = configurations[random.randint(0, len(configurations) - 1)]
codes = list(filter(lambda x: x in characteristics, final_config['configuration']))
conf_size = math.ceil(len(codes) * fullness)
unfishied_config = random.sample(codes, conf_size)
CONFIGURATIONS_UNFINISHED.append(ConfigurationModel({
"configuration": unfishied_config,
"variables": []
}))
return CONFIGURATIONS_UNFINISHED
def get_ratings(requests, finished_configurations, product_structure, scoring_function=None):
if scoring_function == None :
scoring_function = ReduceScoringFunctionFactory.build_scoring_function(
["penalty_ratio", "pref_product_simpleSelectedCharacterstics_average"],
#["pref_average_flat"],
product_structure,
oper = operator.mul
)
list_ofScoreLists = []
for (preference, config) in requests:
list_ofScoreLists.append(list(map(lambda to_rate: scoring_function.calc_score(config, preference, to_rate), finished_configurations)))
return list_ofScoreLists
def plot_at_y(arr, val):
pp.plot(arr, np.zeros_like(arr) + val, 'x')
def get_scores_for_one(configurationState, preference, finished_configurations, product_structure, scoring_function=None):
if scoring_function == None:
scoring_function = ReduceScoringFunctionFactory.build_scoring_function(
["penalty_ratio", "pref_product_simpleSelectedCharacterstics_average"],
product_structure,
oper = operator.mul
)
return list(map(lambda to_rate: scoring_function.calc_score(configurationState, preference, to_rate), finished_configurations))
def get_scoring_functions():
product = ReduceScoringFunctionFactory.build_scoring_function(
["penalty_ratio", "pref_product_simpleSelectedCharacterstics_average"],
product_structure,
oper = operator.mul)
misery = ReduceScoringFunctionFactory.build_scoring_function(
["penalty_ratio", "pref_min_simpleSelectedCharacterstics_average"],
product_structure,
oper = operator.mul)
average = ReduceScoringFunctionFactory.build_scoring_function(
["penalty_ratio", "pref_average_simpleSelectedCharacterstics_average"],
product_structure,
oper = operator.mul)
return [("multiplication",product), ("least misery", misery), ("best average", average)]
def main(amount=1000, fullness=0.1, center=50, threshold_distance_from_centre = 0, group_type='heterogeneous', outdir="./out"):
global CONFIGURATIONS_UNFINISHED
global PREFERENCES_RANDOM_MEMBER
global PREFERENCES_ALL
print("Started Evaluation")
if group_type == 'homogenous':
group_type_mappings = [TYPE_OWNER, TYPE_OWNER, TYPE_OWNER, TYPE_OWNER]
elif group_type == 'random':
group_type_mappings = [TYPE_RANDOM, TYPE_RANDOM, TYPE_RANDOM, TYPE_RANDOM]
else:
group_type='heterogeneous'
group_type_mappings = [TYPE_ATHLETE, TYPE_CONSUMER, TYPE_ENVIRONMENTALIST, TYPE_OWNER]
settings = "amount-{}__center-{}__tdistance-{}__fullness-{}__group-{}".format(amount, center, threshold_distance_from_centre, fullness, group_type)
outdir += "/{}__{}".format(datetime.datetime.utcnow().strftime("%Y_%m_%d_T%H-%M-%S%z"), settings)
# check the directory does not exist
if not(os.path.exists(outdir)):
# create the directory you want to save to
os.mkdir(outdir)
if not(os.path.exists("{}/data".format(outdir))):
os.mkdir("{}/data".format(outdir))
if not(os.path.exists("{}/fig".format(outdir))):
os.mkdir("{}/fig".format(outdir))
random.seed(10924892319)
np.random.seed(seed=956109142)
start_total = start = time.time()
# Generating preferences and unfinished configurations
generate_group_preferences(group_type_mappings, amount=amount)
generate_unfinished_configurations(fullness=0.1, amount = amount)
requests_random_member = list(zip(PREFERENCES_RANDOM_MEMBER, CONFIGURATIONS_UNFINISHED))
requests_all = list(zip(PREFERENCES_ALL, CONFIGURATIONS_UNFINISHED))
end = time.time()
print("Done generating data! It took: {} seconds".format(end - start))
start = time.time()
finished_configurations = list(map(lambda x: ConfigurationModel(x), TinyDB('./evaluation/eval.json').table('CONFIG').all()))
random.shuffle(finished_configurations)
end = time.time()
print("Done loading finished configurations! It took: {} seconds".format(end - start))
scoring_function_list = get_scoring_functions()
results_happiness_db_size_avg_diff = []
results_unhappiness_db_size_avg_diff = []
results_happiness_db_size_avg_total_all = []
results_unhappiness_db_size_avg_total_all = []
piece_counts = [16, 8, 4, 2, 1]
scoring_function_labels = list(map(lambda x: x[0], scoring_function_list))
db_sizes_label = list(map(lambda x: len(finished_configurations) // x, piece_counts))
for label, scoring_function in scoring_function_list:
print("!!! Starting evaluation of: {} !!!".format(label))
# Rate configurations
start = time.time()
np_scores_random = np.array(get_ratings(requests_random_member,finished_configurations,product_structure, scoring_function=scoring_function))
np_scores_all = np.array(get_ratings(requests_all,finished_configurations,product_structure, scoring_function=scoring_function))
end = time.time()
print("Done rating stored configurations! It took: {} seconds".format(end - start))
happiness_db_size_avg_diff = []
unhappiness_db_size_avg_diff = []
happiness_db_size_avg_total_all = []
unhappiness_db_size_avg_total_all = []
happiness_db_size_stdd = []
unhappiness_db_size_stdd = []
for piece_count in piece_counts:
happiness_diff_list = []
unhappiness_diff_list = []
happiness_all_list = []
unhappiness_all_list = []
step_size = len(finished_configurations) // piece_count
residual = len(finished_configurations) % piece_count
for run_count in range(piece_count):
print("Starting run {} of {} with {} as store size.".format(run_count, (piece_count - 1) ,step_size))
offset_start = 0
offset_end = 0
if residual > 0:
residual -= 1
offset_end = 1
start_pos = run_count * step_size + offset_start
end_pos = (run_count + 1) * step_size + offset_start + offset_end
offset_start += offset_end
start = time.time()
# Filtering data
modifier_random = np.zeros(np_scores_random.shape)
modifier_all = np.zeros(np_scores_all.shape)
modifier_random[:,start_pos:end_pos] += 1
modifier_all[:,start_pos:end_pos] += 1
#np_scores_modified_random = np.multiply(np_scores_random[:], modifier_random)
np_scores_modified_random = np_scores_random[:]
np_scores_modified_all = np.multiply(np_scores_all[:], modifier_all)
index_max_random = np.argmax(np_scores_modified_random, axis=1)
index_max_all = np.argmax(np_scores_modified_all, axis=1)
end = time.time()
print("Done getting recommendations! It took: {} seconds".format(end - start))
# Generate individual scores
start = time.time()
scores_individual = [[[] for i in range(len(group_type_mappings))] for i in range(amount)]
j = 0
for preference, configurationState in requests_all:
individuals = preference.getIndividualPreferences()
i = 0
for individual in individuals:
scores_individual[j][i] = get_scores_for_one(configurationState, individual, finished_configurations, product_structure, scoring_function=scoring_function)
i += 1
j += 1
end = time.time()
print("Done generating individual scores! It took: {} seconds".format(end - start))
#Generate hapiness level
start = time.time()
avg_happy_diff = 0
avg_unhappy_diff = 0
avg_happy_all = 0
avg_unhappy_all = 0
individual_index = 0
for individuals_scores in scores_individual:
unhappy_rand = 0
unhappy_all = 0
happy_rand = 0
happy_all = 0
for individual_score in individuals_scores:
np_individual_score = np.array(individual_score)
unhappy_threshold = np.percentile(np_individual_score, center - threshold_distance_from_centre)
happy_threshold = np.percentile(np_individual_score, center + threshold_distance_from_centre)
score_rand = np_individual_score[index_max_random[individual_index]]
score_all = np_individual_score[index_max_all[individual_index]]
if score_all > happy_threshold:
happy_all += 1
elif score_all < unhappy_threshold:
unhappy_all += 1
if score_rand > happy_threshold:
happy_rand += 1
elif score_rand < unhappy_threshold:
unhappy_rand += 1
avg_happy_diff += happy_all - happy_rand
avg_unhappy_diff += unhappy_all - unhappy_rand
avg_happy_all += happy_all
avg_unhappy_all += unhappy_all
individual_index += 1
avg_happy_diff /= amount
avg_unhappy_diff /= amount
avg_happy_all /= amount
avg_unhappy_all /= amount
happiness_diff_list.append(avg_happy_diff)
unhappiness_diff_list.append(avg_unhappy_diff)
happiness_all_list.append(avg_happy_all)
unhappiness_all_list.append(avg_unhappy_all)
print("-- Average increase in happiness: {} | Average increase in unhappiness: {}".format(avg_happy_diff, avg_unhappy_diff))
print("-- Average happiness: {} | Average unhappiness: {}".format(avg_happy_all, avg_unhappy_all))
end = time.time()
print("Done rating recommendations! It took: {} seconds".format(end - start))
happiness_db_size_avg_diff.append(np.average(np.array(happiness_diff_list)))
unhappiness_db_size_avg_diff.append(np.average(np.array(unhappiness_diff_list)))
happiness_db_size_avg_total_all.append(np.average(np.array(happiness_all_list)))
unhappiness_db_size_avg_total_all.append(np.average(np.array(unhappiness_all_list)))
results_happiness_db_size_avg_diff.append(happiness_db_size_avg_diff)
results_unhappiness_db_size_avg_diff.append(unhappiness_db_size_avg_diff)
results_happiness_db_size_avg_total_all.append(happiness_db_size_avg_total_all)
results_unhappiness_db_size_avg_total_all.append(unhappiness_db_size_avg_total_all)
column_names = db_sizes_label
row_names = scoring_function_labels
pd.DataFrame(results_happiness_db_size_avg_diff, index=row_names, columns=column_names).to_csv("{}/data/_happy_increase.csv".format(outdir), index=True, header=True, sep=',')
pd.DataFrame(results_unhappiness_db_size_avg_diff, index=row_names, columns=column_names).to_csv("{}/data/_unhappy_increase.csv".format(outdir).format(outdir), index=True, header=True, sep=',')
pd.DataFrame(results_happiness_db_size_avg_total_all, index=row_names, columns=column_names).to_csv("{}/data/_happy_total_all.csv".format(outdir), index=True, header=True, sep=',')
pd.DataFrame(results_unhappiness_db_size_avg_total_all, index=row_names, columns=column_names).to_csv("{}/data/_unhappy_total_all.csv".format(outdir).format(outdir), index=True, header=True, sep=',')
end_total = time.time()
print("Done! Total time: {} seconds".format(end_total - start_total))
axis=[0,150, -1, 0.5]
pp.figure(figsize=(8,4), dpi=300)
pp.subplots_adjust(hspace = 0.8, wspace=0.4)
pp.subplot(1, 2, 1, title="happiness increase average", )
for result_happy in results_happiness_db_size_avg_diff:
pp.plot(db_sizes_label, result_happy)
pp.legend(scoring_function_labels)
pp.xlabel("number of stored configurations")
pp.ylabel("number of people")
pp.axis(axis)
pp.subplot(1, 2, 2, title="unhappiness increase average")
for result_unhappy in results_unhappiness_db_size_avg_diff:
pp.plot(db_sizes_label, result_unhappy)
pp.legend(scoring_function_labels)
pp.xlabel("number of stored configurations")
pp.ylabel("number of people")
pp.axis(axis)
pp.savefig("{}/fig/happy_unhappy_increase.pdf".format(outdir),format="pdf")
pp.figure(figsize=(8,4), dpi=300)
axis=[0,150, 0, 4]
pp.subplots_adjust(hspace = 0.8, wspace=0.4)
pp.subplot(1, 2, 1, title="happiness absolute average", )
for result_happy in results_happiness_db_size_avg_total_all:
pp.plot(db_sizes_label, result_happy)
pp.legend(scoring_function_labels)
pp.xlabel("number of stored configurations")
pp.ylabel("number of people")
pp.axis(axis)
pp.subplot(1, 2, 2, title="unhappiness absolute average")
for result_unhappy in results_unhappiness_db_size_avg_total_all:
pp.plot(db_sizes_label, result_unhappy)
pp.legend(scoring_function_labels)
pp.xlabel("number of stored configurations")
pp.ylabel("number of people")
pp.axis(axis)
pp.savefig("{}/fig/happy_unhappy_total_all.pdf".format(outdir),format="pdf")
def main_tuple(param):
print("----------------------------------------------------------------------------------------")
print("----------------------Starting: {}----------------------".format(param))
print("----------------------------------------------------------------------------------------")
main(amount=param[0], fullness=param[1],center=param[2] ,threshold_distance_from_centre = param[3], group_type= param[4])
return True
if __name__ == "__main__":
num_cores = multiprocessing.cpu_count()
amounts = [1]
fullnesses = [0.1]
centers = [10, 20, 30, 40, 50, 60, 70, 80, 90]
dists = [5]
g_types = ["heterogeneous", "random", "homogenous"]
params = list(itertools.product(amounts, fullnesses, centers, dists, g_types))
pool = multiprocessing.Pool(processes=num_cores)
res = pool.map(main_tuple, params)

138
evaluation/combinations.py Normal file
View File

@@ -0,0 +1,138 @@
from enum import Enum
class HEIMISCH(Enum):
NIEDRIG = "62bcc15f-5f39-4239-963a-455498c34f79"
MITTEL = "066372f5-9b21-46a3-a62a-76be0afd8f4e"
HOCH = "2e94ef17-7ea1-42e0-b070-bba3a8debfd8"
class KLIMA(Enum):
NIEDRIG = "1a02a295-5afd-427a-bf1e-2b8065687380"
MITTEL = "6f3a5204-1276-40f9-84dc-c8e139e5402d"
HOCH = "5bd172ba-0076-456e-9ee2-0b81780a5da0"
class VERWERTBAR(Enum):
NIEDRIG = "79cb7c2f-6b90-4991-8c9c-9a28f1b31cc8"
MITTEL = "b17eceb1-1cd5-4c11-b398-c6834e4e2ed1"
HOCH = "3fb4ae3d-c892-4025-815f-6b6074ac3d3c"
class AUFWAND(Enum):
NIEDRIG = "a755ba0d-fb8d-475a-a0f9-5ca267fd479f"
MITTEL = "653b4832-6647-426d-a18e-ee444ba67979"
HOCH = "dcb34d08-06f2-4426-a3a9-039cec1e6f6d"
class MENGE(Enum):
NIEDRIG = "9e70ea9a-1311-48db-9238-cbc98da1ed2b"
MITTEL = "5de4ee9e-14a5-4b50-aa36-6efc39cdc24c"
HOCH = "09594573-6fc5-4c62-9d5c-84b4ccf817a1"
class PREIS(Enum):
NIEDRIG = "8d2f5efe-db35-4b4d-9591-cf797335e3ba"
MITTEL = "c7b0f02c-9afe-4ef6-9af0-b7c193b08e93"
HOCH = "c6bd1fd3-8f0f-4d7d-aa8a-86ec00cb7853"
class ERFAHRUNG(Enum):
NIEDRIG = "d59c52cf-0eb1-4ad9-9228-c5100c2b6237"
MITTEL = "311389a2-c4b7-4a13-bf5a-04a1befad0e9"
HOCH = "f08b7cd1-c470-4d6e-951d-a69a02b04849"
not_with = [
(HEIMISCH.MITTEL, KLIMA.HOCH),
(HEIMISCH.MITTEL, VERWERTBAR.HOCH),
(HEIMISCH.HOCH, KLIMA.HOCH),
(HEIMISCH.HOCH, VERWERTBAR.MITTEL),
(HEIMISCH.HOCH, VERWERTBAR.HOCH),
(HEIMISCH.HOCH, MENGE.HOCH),
(HEIMISCH.HOCH, PREIS.NIEDRIG),
(KLIMA.MITTEL, VERWERTBAR.HOCH),
(KLIMA.HOCH, VERWERTBAR.MITTEL),
(KLIMA.HOCH, VERWERTBAR.HOCH),
(KLIMA.HOCH, MENGE.HOCH),
(KLIMA.HOCH, PREIS.MITTEL),
(KLIMA.HOCH, PREIS.NIEDRIG),
(VERWERTBAR.NIEDRIG, MENGE.HOCH),
(VERWERTBAR.NIEDRIG, PREIS.MITTEL),
(VERWERTBAR.NIEDRIG, PREIS.NIEDRIG),
(VERWERTBAR.HOCH, ERFAHRUNG.HOCH),
(AUFWAND.NIEDRIG, MENGE.HOCH),
(AUFWAND.NIEDRIG, PREIS.MITTEL),
(AUFWAND.NIEDRIG, PREIS.NIEDRIG),
(AUFWAND.MITTEL, ERFAHRUNG.MITTEL),
(AUFWAND.MITTEL, ERFAHRUNG.HOCH),
(AUFWAND.HOCH, ERFAHRUNG.MITTEL),
(AUFWAND.HOCH, ERFAHRUNG.HOCH),
(MENGE.NIEDRIG, PREIS.NIEDRIG),
(MENGE.NIEDRIG, PREIS.MITTEL),
(MENGE.MITTEL, PREIS.NIEDRIG),
(MENGE.MITTEL, PREIS.MITTEL),
(MENGE.HOCH, ERFAHRUNG.MITTEL),
(MENGE.HOCH, ERFAHRUNG.HOCH),
]
counter = 0
string = ""
for heimisch in [HEIMISCH.NIEDRIG, HEIMISCH.MITTEL, HEIMISCH.HOCH]:
for klima in [KLIMA.NIEDRIG, KLIMA.MITTEL, KLIMA.HOCH]:
for verwertbar in [VERWERTBAR.NIEDRIG, VERWERTBAR.MITTEL, VERWERTBAR.HOCH]:
for aufwand in [AUFWAND.NIEDRIG, AUFWAND.MITTEL, AUFWAND.HOCH]:
for menge in [MENGE.NIEDRIG, MENGE.MITTEL, MENGE.HOCH]:
for preis in [PREIS.NIEDRIG, PREIS.MITTEL, PREIS.HOCH]:
for erfahrung in [ERFAHRUNG.NIEDRIG, ERFAHRUNG.MITTEL, ERFAHRUNG.HOCH]:
plus = True
if (heimisch, klima) in not_with:
plus = False
if (heimisch, verwertbar) in not_with:
plus = False
if (heimisch, aufwand) in not_with:
plus = False
if (heimisch, menge) in not_with:
plus = False
if (heimisch, preis) in not_with:
plus = False
if (heimisch, erfahrung) in not_with:
plus = False
if (klima, verwertbar) in not_with:
plus = False
if (klima, aufwand) in not_with:
plus = False
if (klima, menge) in not_with:
plus = False
if (klima, preis) in not_with:
plus = False
if (klima, erfahrung) in not_with:
plus = False
if (verwertbar, aufwand) in not_with:
plus = False
if (verwertbar, menge) in not_with:
plus = False
if (verwertbar, preis) in not_with:
plus = False
if (verwertbar, erfahrung) in not_with:
plus = False
if (aufwand, menge) in not_with:
plus = False
if (aufwand, preis) in not_with:
plus = False
if (aufwand, erfahrung) in not_with:
plus = False
if (menge, preis) in not_with:
plus = False
if (menge, erfahrung) in not_with:
plus = False
if (preis, erfahrung) in not_with:
plus = False
if plus:
counter += 1
print("{}, {}, {}, {}, {}, {}, {}".format(heimisch, klima, verwertbar, aufwand, menge, preis, erfahrung))
string += '"' + str(counter) + '":' + "{ 'configuration': [" + heimisch.value + "," + klima.value + "," + verwertbar.value + "," + aufwand.value + "," + menge.value + "," + preis.value + "," + erfahrung.value + "], 'variables': []},"
print(counter)
#print(string)

1
evaluation/eval.json Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,223 @@
from enum import Enum
import numpy as np
import random
class Attitude(Enum):
NEUTRAL = (0.5, 0.05)
POSITIVE = (0.75, 0.1)
NEGATIVE = (0.25, 0.1)
class NormalDistribution:
def __init__(self, attitude : Attitude):
self.mean = attitude.value[0]
self.std_deviation = attitude.value[1]
def generateNumber(self):
val = np.random.normal(loc=self.mean, scale=self.std_deviation)
val = val * 2 - 1 #value has to be changed to [-1,1]
if val > 1:
return 1
elif val < -1:
return -1
else:
return val
class UniformDistribution:
def __init__(self, min_val=-1, max_val=1):
self.min_val = min_val
self.max_val = max_val
def generateNumber(self):
val = random.uniform(self.min_val, self.max_val)
return val
TYPE_RANDOM = {
"name": "random",
# local trees (3c0b0b79-2b8c-4df9-95b9-4505443b3638)
"62bcc15f-5f39-4239-963a-455498c34f79": UniformDistribution(), #Low
"066372f5-9b21-46a3-a62a-76be0afd8f4e": UniformDistribution(), #Medium
"2e94ef17-7ea1-42e0-b070-bba3a8debfd8": UniformDistribution(), #High
# climate resilient trees (290951c4-ed76-4d5c-8d26-0ecc8ca42e59)
"1a02a295-5afd-427a-bf1e-2b8065687380": UniformDistribution(), #Low
"6f3a5204-1276-40f9-84dc-c8e139e5402d": UniformDistribution(), #Medium
"5bd172ba-0076-456e-9ee2-0b81780a5da0": UniformDistribution(), #High
# usable trees (5fee0b16-9ba2-4162-a98b-5c6170ab200e)
"79cb7c2f-6b90-4991-8c9c-9a28f1b31cc8": UniformDistribution(), #Low
"b17eceb1-1cd5-4c11-b398-c6834e4e2ed1": UniformDistribution(), #Medium
"3fb4ae3d-c892-4025-815f-6b6074ac3d3c": UniformDistribution(), #High
# harvesting effort (8c7bd4d7-b91e-4518-99d4-7564df1d4207)
"a755ba0d-fb8d-475a-a0f9-5ca267fd479f": UniformDistribution(), # Manual
"653b4832-6647-426d-a18e-ee444ba67979": UniformDistribution(), # Harvester
"dcb34d08-06f2-4426-a3a9-039cec1e6f6d": UniformDistribution(), # Self-driving Harvester
# harvesting amount (c5b446ce-455a-44b5-b8be-178eef2848c2)
"9e70ea9a-1311-48db-9238-cbc98da1ed2b": UniformDistribution(), #No harvest
"5de4ee9e-14a5-4b50-aa36-6efc39cdc24c": UniformDistribution(), #Low harvest
"09594573-6fc5-4c62-9d5c-84b4ccf817a1": UniformDistribution(), #High harvest
# wood price (3a375746-288f-4147-8065-9f6966389772)
"8d2f5efe-db35-4b4d-9591-cf797335e3ba": UniformDistribution(), #Low
"c7b0f02c-9afe-4ef6-9af0-b7c193b08e93": UniformDistribution(), #Medium
"c6bd1fd3-8f0f-4d7d-aa8a-86ec00cb7853": UniformDistribution(), #High
# public accessability (5d8ab41c-02bd-4478-9106-64e80bdb9728)
"d59c52cf-0eb1-4ad9-9228-c5100c2b6237": UniformDistribution(), #Almost none
"311389a2-c4b7-4a13-bf5a-04a1befad0e9": UniformDistribution(), #Low intensity
"f08b7cd1-c470-4d6e-951d-a69a02b04849": UniformDistribution(), #High intensity
}
TYPE_ATHLETE = {
"name": "athlete",
# local trees (3c0b0b79-2b8c-4df9-95b9-4505443b3638)
"62bcc15f-5f39-4239-963a-455498c34f79": NormalDistribution(Attitude.NEGATIVE), #Low
"066372f5-9b21-46a3-a62a-76be0afd8f4e": NormalDistribution(Attitude.POSITIVE), #Medium
"2e94ef17-7ea1-42e0-b070-bba3a8debfd8": NormalDistribution(Attitude.POSITIVE), #High
# climate resilient trees (290951c4-ed76-4d5c-8d26-0ecc8ca42e59)
"1a02a295-5afd-427a-bf1e-2b8065687380": NormalDistribution(Attitude.NEUTRAL), #Low
"6f3a5204-1276-40f9-84dc-c8e139e5402d": NormalDistribution(Attitude.POSITIVE), #Medium
"5bd172ba-0076-456e-9ee2-0b81780a5da0": NormalDistribution(Attitude.POSITIVE), #High
# usable trees (5fee0b16-9ba2-4162-a98b-5c6170ab200e)
"79cb7c2f-6b90-4991-8c9c-9a28f1b31cc8": NormalDistribution(Attitude.NEUTRAL), #Low
"b17eceb1-1cd5-4c11-b398-c6834e4e2ed1": NormalDistribution(Attitude.NEUTRAL), #Medium
"3fb4ae3d-c892-4025-815f-6b6074ac3d3c": NormalDistribution(Attitude.NEGATIVE), #High
# harvesting effort (8c7bd4d7-b91e-4518-99d4-7564df1d4207)
"a755ba0d-fb8d-475a-a0f9-5ca267fd479f": NormalDistribution(Attitude.NEUTRAL), # Manual
"653b4832-6647-426d-a18e-ee444ba67979": NormalDistribution(Attitude.NEGATIVE), # Harvester
"dcb34d08-06f2-4426-a3a9-039cec1e6f6d": NormalDistribution(Attitude.NEGATIVE), # Self-driving Harvester
# harvesting amount (c5b446ce-455a-44b5-b8be-178eef2848c2)
"9e70ea9a-1311-48db-9238-cbc98da1ed2b": NormalDistribution(Attitude.POSITIVE), #No harvest
"5de4ee9e-14a5-4b50-aa36-6efc39cdc24c": NormalDistribution(Attitude.NEUTRAL), #Low harvest
"09594573-6fc5-4c62-9d5c-84b4ccf817a1": NormalDistribution(Attitude.NEGATIVE), #High harvest
# wood price (3a375746-288f-4147-8065-9f6966389772)
"8d2f5efe-db35-4b4d-9591-cf797335e3ba": NormalDistribution(Attitude.NEUTRAL), #Low
"c7b0f02c-9afe-4ef6-9af0-b7c193b08e93": NormalDistribution(Attitude.NEUTRAL), #Medium
"c6bd1fd3-8f0f-4d7d-aa8a-86ec00cb7853": NormalDistribution(Attitude.NEUTRAL), #High
# public accessability (5d8ab41c-02bd-4478-9106-64e80bdb9728)
"d59c52cf-0eb1-4ad9-9228-c5100c2b6237": NormalDistribution(Attitude.NEGATIVE), #Almost none
"311389a2-c4b7-4a13-bf5a-04a1befad0e9": NormalDistribution(Attitude.NEUTRAL), #Low intensity
"f08b7cd1-c470-4d6e-951d-a69a02b04849": NormalDistribution(Attitude.POSITIVE), #High intensity
}
TYPE_OWNER = {
"name": "owner",
# local trees (3c0b0b79-2b8c-4df9-95b9-4505443b3638)
"62bcc15f-5f39-4239-963a-455498c34f79": NormalDistribution(Attitude.POSITIVE), #Low
"066372f5-9b21-46a3-a62a-76be0afd8f4e": NormalDistribution(Attitude.NEUTRAL), #Medium
"2e94ef17-7ea1-42e0-b070-bba3a8debfd8": NormalDistribution(Attitude.NEGATIVE), #High
# climate resilient trees (290951c4-ed76-4d5c-8d26-0ecc8ca42e59)
"1a02a295-5afd-427a-bf1e-2b8065687380": NormalDistribution(Attitude.POSITIVE), #Low
"6f3a5204-1276-40f9-84dc-c8e139e5402d": NormalDistribution(Attitude.NEUTRAL), #Medium
"5bd172ba-0076-456e-9ee2-0b81780a5da0": NormalDistribution(Attitude.NEGATIVE), #High
# usable trees (5fee0b16-9ba2-4162-a98b-5c6170ab200e)
"79cb7c2f-6b90-4991-8c9c-9a28f1b31cc8": NormalDistribution(Attitude.NEUTRAL), #Low
"b17eceb1-1cd5-4c11-b398-c6834e4e2ed1": NormalDistribution(Attitude.NEUTRAL), #Medium
"3fb4ae3d-c892-4025-815f-6b6074ac3d3c": NormalDistribution(Attitude.POSITIVE), #High
# harvesting effort (8c7bd4d7-b91e-4518-99d4-7564df1d4207)
"a755ba0d-fb8d-475a-a0f9-5ca267fd479f": NormalDistribution(Attitude.NEUTRAL), # Manual
"653b4832-6647-426d-a18e-ee444ba67979": NormalDistribution(Attitude.POSITIVE), # Harvester
"dcb34d08-06f2-4426-a3a9-039cec1e6f6d": NormalDistribution(Attitude.POSITIVE), # Self-driving Harvester
# harvesting amount (c5b446ce-455a-44b5-b8be-178eef2848c2)
"9e70ea9a-1311-48db-9238-cbc98da1ed2b": NormalDistribution(Attitude.NEGATIVE), #No harvest
"5de4ee9e-14a5-4b50-aa36-6efc39cdc24c": NormalDistribution(Attitude.POSITIVE), #Low harvest
"09594573-6fc5-4c62-9d5c-84b4ccf817a1": NormalDistribution(Attitude.POSITIVE), #High harvest
# wood price (3a375746-288f-4147-8065-9f6966389772)
"8d2f5efe-db35-4b4d-9591-cf797335e3ba": NormalDistribution(Attitude.NEUTRAL), #Low
"c7b0f02c-9afe-4ef6-9af0-b7c193b08e93": NormalDistribution(Attitude.POSITIVE), #Medium
"c6bd1fd3-8f0f-4d7d-aa8a-86ec00cb7853": NormalDistribution(Attitude.POSITIVE), #High
# public accessability (5d8ab41c-02bd-4478-9106-64e80bdb9728)
"d59c52cf-0eb1-4ad9-9228-c5100c2b6237": NormalDistribution(Attitude.POSITIVE), #Almost none
"311389a2-c4b7-4a13-bf5a-04a1befad0e9": NormalDistribution(Attitude.NEUTRAL), #Low intensity
"f08b7cd1-c470-4d6e-951d-a69a02b04849": NormalDistribution(Attitude.NEGATIVE), #High intensity
}
TYPE_ENVIRONMENTALIST = {
"name": "environmentalist",
# local trees (3c0b0b79-2b8c-4df9-95b9-4505443b3638)
"62bcc15f-5f39-4239-963a-455498c34f79": NormalDistribution(Attitude.NEGATIVE), #Low
"066372f5-9b21-46a3-a62a-76be0afd8f4e": NormalDistribution(Attitude.NEGATIVE), #Medium
"2e94ef17-7ea1-42e0-b070-bba3a8debfd8": NormalDistribution(Attitude.POSITIVE), #High
# climate resilient trees (290951c4-ed76-4d5c-8d26-0ecc8ca42e59)
"1a02a295-5afd-427a-bf1e-2b8065687380": NormalDistribution(Attitude.NEUTRAL), #Low
"6f3a5204-1276-40f9-84dc-c8e139e5402d": NormalDistribution(Attitude.NEUTRAL), #Medium
"5bd172ba-0076-456e-9ee2-0b81780a5da0": NormalDistribution(Attitude.NEGATIVE), #High
# usable trees (5fee0b16-9ba2-4162-a98b-5c6170ab200e)
"79cb7c2f-6b90-4991-8c9c-9a28f1b31cc8": NormalDistribution(Attitude.NEUTRAL), #Low
"b17eceb1-1cd5-4c11-b398-c6834e4e2ed1": NormalDistribution(Attitude.NEGATIVE), #Medium
"3fb4ae3d-c892-4025-815f-6b6074ac3d3c": NormalDistribution(Attitude.NEGATIVE), #High
# harvesting effort (8c7bd4d7-b91e-4518-99d4-7564df1d4207)
"a755ba0d-fb8d-475a-a0f9-5ca267fd479f": NormalDistribution(Attitude.POSITIVE), # Manual
"653b4832-6647-426d-a18e-ee444ba67979": NormalDistribution(Attitude.NEGATIVE), # Harvester
"dcb34d08-06f2-4426-a3a9-039cec1e6f6d": NormalDistribution(Attitude.NEGATIVE), # Self-driving Harvester
# harvesting amount (c5b446ce-455a-44b5-b8be-178eef2848c2)
"9e70ea9a-1311-48db-9238-cbc98da1ed2b": NormalDistribution(Attitude.POSITIVE), #No harvest
"5de4ee9e-14a5-4b50-aa36-6efc39cdc24c": NormalDistribution(Attitude.NEUTRAL), #Low harvest
"09594573-6fc5-4c62-9d5c-84b4ccf817a1": NormalDistribution(Attitude.NEGATIVE), #High harvest
# wood price (3a375746-288f-4147-8065-9f6966389772)
"8d2f5efe-db35-4b4d-9591-cf797335e3ba": NormalDistribution(Attitude.NEUTRAL), #Low
"c7b0f02c-9afe-4ef6-9af0-b7c193b08e93": NormalDistribution(Attitude.NEUTRAL), #Medium
"c6bd1fd3-8f0f-4d7d-aa8a-86ec00cb7853": NormalDistribution(Attitude.NEUTRAL), #High
# public accessability (5d8ab41c-02bd-4478-9106-64e80bdb9728)
"d59c52cf-0eb1-4ad9-9228-c5100c2b6237": NormalDistribution(Attitude.POSITIVE), #Almost none
"311389a2-c4b7-4a13-bf5a-04a1befad0e9": NormalDistribution(Attitude.NEUTRAL), #Low intensity
"f08b7cd1-c470-4d6e-951d-a69a02b04849": NormalDistribution(Attitude.NEGATIVE), #High intensity
}
TYPE_CONSUMER = {
"name": "consumer",
# local trees (3c0b0b79-2b8c-4df9-95b9-4505443b3638)
"62bcc15f-5f39-4239-963a-455498c34f79": NormalDistribution(Attitude.NEUTRAL), #Low
"066372f5-9b21-46a3-a62a-76be0afd8f4e": NormalDistribution(Attitude.NEUTRAL), #Medium
"2e94ef17-7ea1-42e0-b070-bba3a8debfd8": NormalDistribution(Attitude.NEGATIVE), #High
# climate resilient trees (290951c4-ed76-4d5c-8d26-0ecc8ca42e59)
"1a02a295-5afd-427a-bf1e-2b8065687380": NormalDistribution(Attitude.NEUTRAL), #Low
"6f3a5204-1276-40f9-84dc-c8e139e5402d": NormalDistribution(Attitude.NEUTRAL), #Medium
"5bd172ba-0076-456e-9ee2-0b81780a5da0": NormalDistribution(Attitude.NEGATIVE), #High
# usable trees (5fee0b16-9ba2-4162-a98b-5c6170ab200e)
"79cb7c2f-6b90-4991-8c9c-9a28f1b31cc8": NormalDistribution(Attitude.NEGATIVE), #Low
"b17eceb1-1cd5-4c11-b398-c6834e4e2ed1": NormalDistribution(Attitude.NEUTRAL), #Medium
"3fb4ae3d-c892-4025-815f-6b6074ac3d3c": NormalDistribution(Attitude.POSITIVE), #High
# harvesting effort (8c7bd4d7-b91e-4518-99d4-7564df1d4207)
"a755ba0d-fb8d-475a-a0f9-5ca267fd479f": NormalDistribution(Attitude.NEGATIVE), # Manual
"653b4832-6647-426d-a18e-ee444ba67979": NormalDistribution(Attitude.NEUTRAL), # Harvester
"dcb34d08-06f2-4426-a3a9-039cec1e6f6d": NormalDistribution(Attitude.NEUTRAL), # Self-driving Harvester
# harvesting amount (c5b446ce-455a-44b5-b8be-178eef2848c2)
"9e70ea9a-1311-48db-9238-cbc98da1ed2b": NormalDistribution(Attitude.NEGATIVE), #No harvest
"5de4ee9e-14a5-4b50-aa36-6efc39cdc24c": NormalDistribution(Attitude.NEGATIVE), #Low harvest
"09594573-6fc5-4c62-9d5c-84b4ccf817a1": NormalDistribution(Attitude.POSITIVE), #High harvest
# wood price (3a375746-288f-4147-8065-9f6966389772)
"8d2f5efe-db35-4b4d-9591-cf797335e3ba": NormalDistribution(Attitude.POSITIVE), #Low
"c7b0f02c-9afe-4ef6-9af0-b7c193b08e93": NormalDistribution(Attitude.NEUTRAL), #Medium
"c6bd1fd3-8f0f-4d7d-aa8a-86ec00cb7853": NormalDistribution(Attitude.NEGATIVE), #High
# public accessability (5d8ab41c-02bd-4478-9106-64e80bdb9728)
"d59c52cf-0eb1-4ad9-9228-c5100c2b6237": NormalDistribution(Attitude.NEUTRAL), #Almost none
"311389a2-c4b7-4a13-bf5a-04a1befad0e9": NormalDistribution(Attitude.NEUTRAL), #Low intensity
"f08b7cd1-c470-4d6e-951d-a69a02b04849": NormalDistribution(Attitude.NEUTRAL), #High intensity
}

4
profiler.py Normal file
View File

@@ -0,0 +1,4 @@
import cProfile
import eval
cProfile.run('eval.main()')

7
requirements.txt Normal file
View File

@@ -0,0 +1,7 @@
flask-restplus
tinydb
tinyrecord
numpy
pytest
coverage
pandas

1
run.bat Normal file
View File

@@ -0,0 +1 @@
python ./src/app.py

3
run_coverage.bat Normal file
View File

@@ -0,0 +1,3 @@
coverage run --source "./src" -m pytest
coverage report -m
PAUSE

1
run_eval.bat Normal file
View File

@@ -0,0 +1 @@
python ./eval.py

2
run_tests.bat Normal file
View File

@@ -0,0 +1,2 @@
pytest
PAUSE

1
run_vis.bat Normal file
View File

@@ -0,0 +1 @@
python ./vis.py

15
src/apis/__init__.py Normal file
View File

@@ -0,0 +1,15 @@
from flask_restplus import Api
from .config import api as config_api
from .recommender import api as recommender_api
from .product_structure import api as prod_structure_api
api = Api(
title='Configuration Recommendation API',
version='1.0',
description='A simple recommendation API',
)
api.add_namespace(recommender_api)
api.add_namespace(config_api)
api.add_namespace(prod_structure_api)

35
src/apis/config.py Normal file
View File

@@ -0,0 +1,35 @@
from flask_restplus import Namespace, Resource, fields
from daos.config_dao import ConfigurationDAO
api = Namespace('config', description='Configuration related operations')
variable_model = api.model('config_variable', {
'value': fields.Float(required=True, description='The contained variable value'),
'code': fields.String(required=True, description='The contained variable code')
})
config_model = api.model('config', {
'configuration': fields.List(fields.String(), required=True, description='The contained codes'),
'variables': fields.List(fields.Nested(variable_model), required=True, description='The contained variables')
})
@api.route('/')
class ConfigList(Resource):
@api.doc('list_configs')
@api.marshal_list_with(config_model)
def get(self):
'''List all stored configurations'''
return ConfigurationDAO.getInstance().getAll()
@api.doc('add_config')
@api.expect(config_model)
@api.marshal_with(config_model, code=201)
def post(self):
'''Put configuration'''
config = api.payload
if not ConfigurationDAO.getInstance().exists(config):
ConfigurationDAO.getInstance().add(api.payload)
return api.payload, 201

View File

@@ -0,0 +1,29 @@
from flask_restplus import Namespace, Resource, fields
from daos.product_structure_dao import ProductStructureDAO
import copy
api = Namespace('product_structure', description='Product structure related operations')
product_structure_model = api.model('Product Structure', {
#'ProductStructure': fields.List(fields.Wildcard(), required=True, example="{}", description='The array containing the product structure elements'),
})
DAO = ProductStructureDAO.getInstance()
@api.route('/')
class ProductStructure(Resource):
@api.doc('show_product_structure')
#@api.marshal_list_with(product_structure_model)
def get(self):
'''Return product structure'''
return DAO.get()
@api.doc('replace_product_structure')
@api.expect(product_structure_model)
#@api.marshal_with(product_structure_model, code=201)
def put(self):
'''replace product structure'''
DAO.replace(api.payload)
return DAO.get(), 201

38
src/apis/recommender.py Normal file
View File

@@ -0,0 +1,38 @@
from flask_restplus import Namespace, Resource, fields
from .config import config_model
from managers.recommendation_manager import RecommendationManager
from model.configuration_model import ConfigurationModel
from model.preferences_model import Preferences
api = Namespace('recommender', description='Recommendation related operations')
rating_model = api.model('Rating', {
'code': fields.String(required=True, description='The code that was rated'),
'value': fields.Float(required=True, description='The rating value'),
})
preference_model = api.model('Preference', {
'user': fields.String(required=True, description='The user identifier'),
'ratings': fields.List(fields.Nested(rating_model),required=True, description='The list of ratings of this user'),
})
recommendation_request_model = api.model('Recommendation Request', {
'configuration': fields.Nested(config_model, required=True, description='The user identifier'),
'preferences': fields.List(fields.Nested(preference_model),required=True, description='The list of ratings of this user'),
})
@api.route('/')
class Recommendation(Resource):
manager = RecommendationManager()
@api.doc('get_recommendation')
@api.expect(recommendation_request_model)
@api.marshal_list_with(config_model)
def post(self):
'''Get recommendation'''
result = self.manager.getRecommendation(Preferences(api.payload), ConfigurationModel(api.payload['configuration']))
response = result
return response

11
src/app.py Normal file
View File

@@ -0,0 +1,11 @@
from flask import Flask
from werkzeug.middleware.proxy_fix import ProxyFix
from apis import api
app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app)
api.init_app(app)
app.run(debug=True)

0
src/conftest.py Normal file
View File

0
src/daos/__init__.py Normal file
View File

38
src/daos/config_dao.py Normal file
View File

@@ -0,0 +1,38 @@
from daos.db import DB_CONFIG
from tinyrecord import transaction
from model.configuration_model import ConfigurationModel
class ConfigurationDAO:
__instance = None
def add(self, config):
trans = DB_CONFIG()
with transaction(trans) as tr:
tr.insert(config)
def getAll(self):
return DB_CONFIG().all()
def getAll_as_objects(self):
configurations = []
for conf in self.getAll():
configurations.append(ConfigurationModel(conf))
def exists(self, config):
for conf in DB_CONFIG().all():
if len(set(conf['configuration']).symmetric_difference(set(config['configuration']))) == 0:
return True
return False
@staticmethod
def getInstance():
""" Static access method. """
if ConfigurationDAO.__instance == None:
ConfigurationDAO()
return ConfigurationDAO.__instance
def __init__(self):
""" Virtually private constructor. """
if ConfigurationDAO.__instance != None:
raise Exception("This class is a singleton!")
else:
ConfigurationDAO.__instance = self

10
src/daos/db.py Normal file
View File

@@ -0,0 +1,10 @@
from tinydb import TinyDB
def DB():
return TinyDB('db.json')
def DB_CONFIG():
return DB().table('CONFIG')
def DB_PRODUCT_STRUCTURE():
return DB().table('PRODUCT_STRUCTURE')

View File

@@ -0,0 +1,48 @@
from daos.db import DB_PRODUCT_STRUCTURE
from tinydb import Query
from tinyrecord import transaction
from model.product_structure_model import ProductStructureModel
rmList = []
class ProductStructureDAO(object):
__instance = None
def get_as_objects(self) -> ProductStructureModel:
return ProductStructureModel(self.get())
def get(self):
highest_id = self._get_highest_id()
return DB_PRODUCT_STRUCTURE().get(doc_id=highest_id)
def replace(self, structure):
highest_id = self._get_highest_id()
rmList = list(range(0,highest_id + 1))
trans = DB_PRODUCT_STRUCTURE()
with transaction(trans) as tr:
tr.insert(structure)
tr.remove(doc_ids=rmList)
return structure
def _get_highest_id(self):
all = DB_PRODUCT_STRUCTURE().all()
max = 0
for element in all:
if element.doc_id > max:
max = element.doc_id
return max
@staticmethod
def getInstance():
""" Static access method. """
if ProductStructureDAO.__instance == None:
ProductStructureDAO()
return ProductStructureDAO.__instance
def __init__(self):
""" Virtually private constructor. """
if ProductStructureDAO.__instance != None:
raise Exception("This class is a singleton!")
else:
ProductStructureDAO.__instance = self

0
src/managers/__init__.py Normal file
View File

View File

@@ -0,0 +1,89 @@
from daos.config_dao import ConfigurationDAO
from daos.product_structure_dao import ProductStructureDAO
from model.configuration_model import ConfigurationModel
from model.preferences_model import Preferences
from model.product_structure_model import ProductStructureModel
from scoring.scoring_functions import ReduceScoringFunctionFactory, ScoringFunction
import numpy as np
import operator
class RecommendationManager:
def getRecommendation(self, preferences: Preferences , current_config : ConfigurationModel,
scoring_methods = "avg",
penalty_function = "penalty_ratio",
product_structure = ProductStructureDAO.getInstance().get_as_objects(),
configurations = ConfigurationDAO.getInstance().getAll()):
avg = ReduceScoringFunctionFactory.build_scoring_function(
[penalty_function, "pref_average_simpleSelectedCharacterstics_average"],
product_structure,
oper = operator.mul
)
lm = ReduceScoringFunctionFactory.build_scoring_function(
[penalty_function, "pref_min_simpleSelectedCharacterstics_average"],
product_structure,
oper = operator.mul
)
multi = ReduceScoringFunctionFactory.build_scoring_function(
[penalty_function, "pref_product_simpleSelectedCharacterstics_average"],
product_structure,
oper = operator.mul
)
default = SimpleConfigurationMaxSelector( avg )
switcher = {
"avg" : default,
"multi": SimpleConfigurationMaxSelector(multi),
"lm": SimpleConfigurationMaxSelector( lm ),
"avg-lm": PipeFilterMax(ConfigurationFilter(avg), SimpleConfigurationMaxSelector( lm )),
"lm-avg": PipeFilterMax(ConfigurationFilter(lm), SimpleConfigurationMaxSelector( avg ))
}
max_selector = switcher.get(scoring_methods, default)
return max_selector.getMax(preferences, current_config, configurations)
class ConfigurationMaxSelector:
def getMax(self, preferences: Preferences, current_config : ConfigurationModel, configurations):
pass
class PipeFilterMax(ConfigurationMaxSelector):
def __init__(self, configuration_filter : 'ConfigurationFilter', max_selector : ConfigurationMaxSelector):
self.configuration_filter = configuration_filter
self.max_selector = max_selector
def getMax(self, preferences: Preferences, current_config : ConfigurationModel, configurations):
list = self.configuration_filter.filter(preferences, current_config, configurations)
return self.max_selector.getMax(preferences, current_config, list)
class ConfigurationFilter:
def __init__(self, scoring_function : ScoringFunction, percentile = 50):
assert percentile <= 100
assert percentile >= 0
self.scoring_function = scoring_function
self.percentile = percentile
def filter(self,
preferences: Preferences,
current_config : ConfigurationModel,
configurations):
scores = list(map(lambda x: self.scoring_function.calc_score(current_config, preferences, ConfigurationModel(x)), configurations))
barrier = np.percentile(np.array(scores), self.percentile)
return list(filter(lambda x: self.scoring_function.calc_score(current_config, preferences, ConfigurationModel(x)) > barrier, configurations))
class SimpleConfigurationMaxSelector(ConfigurationMaxSelector):
def __init__(self, scoring_function : ScoringFunction):
self.scoring_function = scoring_function
def getMax(self, preferences: Preferences, current_config : ConfigurationModel, configurations):
best_rating = float("-inf")
best = None
for to_rate in configurations:
score = self.scoring_function.calc_score(current_config, preferences, ConfigurationModel(to_rate))
if score > best_rating:
best = to_rate
best_rating = score
print('Best rating: {}'.format(best_rating))
return best

0
src/model/__init__.py Normal file
View File

View File

@@ -0,0 +1,17 @@
from typing import List
class ConfigurationVariablesModel:
def __init__(self, data):
self.value : str = data['value']
self.code : str = data['code']
class ConfigurationModel:
def __init__(self, data):
self.configuration : List[str] = []
self.variables : List[ConfigurationVariablesModel] = []
if data is not None:
self.configuration = data['configuration']
if 'variables' in data:
for v in data['variables']:
self.variables.append(ConfigurationVariablesModel(v))

View File

@@ -0,0 +1,57 @@
from typing import List
class Rating:
def __init__(self, data):
self.code = data['code']
self.value = float(data['value'])
if self.value < 0 or self.value > 1:
raise ValueError("Value of rating has to be in interval [0,1]")
def getValue(self):
""" Returns rating value """
return self.value
class UserPreference:
def __init__(self, data):
self.ratings : List[Rating] = []
self.user : str = data['user']
for rat in data['ratings']:
self.ratings.append(Rating(rat))
def getAllRatings(self) -> List[Rating]:
return self.ratings
def getRatingByCode(self, code : str) -> Rating:
return next(filter(lambda x : x.code == code, self.ratings), Rating({'code': code, 'value': 0.5 }))
class Preferences:
def __init__(self, data={ 'preferences' : [] }):
self.preferences : List[UserPreference] = []
for pref in data['preferences']:
self.preferences.append(UserPreference(pref))
def getAllUserPreferences(self) -> List[UserPreference]:
return self.preferences
def getAllRatingsByCode(self, code) -> List[Rating]:
list = []
for user_pref in self.preferences:
list.append(user_pref.getRatingByCode('code'))
return list
def getAllUsers(self) -> List[str] :
list = []
for userPref in self.preferences:
if userPref.user not in list :
list.append(userPref.user)
return list
def getRatingValueByUserAndCode(self, user, code) -> float:
for userPref in self.preferences:
if userPref.user == user :
for rating in userPref.ratings:
if rating.code == code:
return rating.getValue()
return 0.5
def getIndividualPreferences(self):
return list(map(lambda x: _create_preferences_and_add_user_pref(x), self.preferences))
def _create_preferences_and_add_user_pref(userPref):
tmp = Preferences()
tmp.preferences.append(userPref)
return tmp

View File

@@ -0,0 +1,71 @@
from typing import List
from enum import Enum
class ProductStructureTypeEnum(Enum):
CHARACTERISTIC = "CHARACTERISTIC",
FEATURE = "FEATURE",
CLUSTER = "CLUSTER",
VARIABLE = "VARIABLE",
class ProductStructureElementModel:
def __init__(self, data):
self.children = []
self.type = None
self.elementId = data['elementId']
self.type = data['type']
self.name = data['name']
self.additionalData = data['additionalData']
for element in data['children']:
self.children.append(ProductStructureElementModel(element))
def get_list_of_all(self, type : ProductStructureTypeEnum) :
tmp_list = []
for child in self.children :
tmp_list = tmp_list + child.get_list_of_all(type)
if ProductStructureTypeEnum[self.type] == type:
tmp_list.append(self)
return tmp_list
def get_children_characteristics(self):
tmp_list = self.get_list_of_all(ProductStructureTypeEnum.CHARACTERISTIC)
if self in tmp_list:
tmp_list.remove(self)
return tmp_list
class ProductStructureModel:
list_of_features = []
list_of_characteristics = []
def __init__(self, data):
self.productStructure : List[ProductStructureElementModel] = []
for element in data['ProductStructure']:
child = ProductStructureElementModel(element)
self.productStructure.append(child)
def get_list_of_features(self):
if (self.list_of_features == []) :
tmp_list = []
for element in self.productStructure:
tmp_list = tmp_list + element.get_list_of_all(ProductStructureTypeEnum.FEATURE)
self.list_of_features = tmp_list
return self.list_of_features
def get_list_of_characteristics(self):
if (self.list_of_features == []) :
tmp_list = []
for element in self.productStructure:
tmp_list = tmp_list + element.get_list_of_all(ProductStructureTypeEnum.CHARACTERISTIC)
self.list_of_characteristics = tmp_list
return self.list_of_characteristics
def isCharacteristic(self, code: str) -> bool:
list = self.get_list_of_characteristics()
return any(map(lambda x: x.elementId == code , list))

0
src/scoring/__init__.py Normal file
View File

View File

@@ -0,0 +1,30 @@
from typing import List
from functools import reduce
import operator
class ListFunction:
pass
class ListToListFunction(ListFunction):
def applyToList(self, list : List[float]) -> List[float]:
pass
class ListToValueFunction(ListFunction):
def convertToFloat(self, list : List[float]) -> float:
pass
class Average(ListToValueFunction):
def convertToFloat(self, list : List[float]) -> float:
score = len(list)
if score == 0:
score = 1
if list:
return reduce(operator.add, list) / score
else:
return 0.0
class Min(ListToValueFunction):
def convertToFloat(self, list : List[float]) -> float:
return min(list)
class Product(ListToValueFunction):
def convertToFloat(self, list : List[float]) -> float:
return reduce(operator.mul, list)

View File

@@ -0,0 +1,76 @@
from typing import List
from model.preferences_model import Preferences, Rating
from model.configuration_model import ConfigurationModel
from model.product_structure_model import ProductStructureModel
from scoring.list_functions import ListToValueFunction
from scoring.value_functions import MapToPercent
class PreferencesToListFunction:
def convertToList(self, preferences : Preferences, toRate : ConfigurationModel) -> List[float]:
return []
class FlattenPreferencesToListFunction(PreferencesToListFunction):
def convertToList(self, preferences : Preferences, toRate : ConfigurationModel) -> List[float]:
list : List[Rating] = []
for user_pref in preferences.getAllUserPreferences():
for rating in user_pref.getAllRatings():
if rating.code in toRate.configuration:
list.append(rating.getValue())
else :
list.append(1 - rating.getValue())
return list
class SimplePerUserToListFunction(PreferencesToListFunction):
def __init__(self, listToValue : ListToValueFunction):
self.listToValueFunction = listToValue
def convertToList(self, preferences : Preferences, toRate : ConfigurationModel) -> List[float]:
list = []
for user_pref in preferences.getAllUserPreferences():
user_list : List[float] = []
for rating in user_pref.getAllRatings():
if rating.code in toRate.configuration:
user_list.append(rating.getValue())
else :
user_list.append(1 - rating.getValue())
list.append(self.listToValueFunction.convertToFloat(user_list))
return list
class SimpleSelectedCharacteristicsToListFunction(PreferencesToListFunction):
def __init__(self, listToValue : ListToValueFunction):
self.listToValueFunction = listToValue
def convertToList(self, preferences : Preferences, toRate : ConfigurationModel) -> List[float]:
list = []
for user_pref in preferences.getAllUserPreferences():
user_list : List[float] = []
for code in toRate.configuration:
user_list.append(user_pref.getRatingByCode(code).getValue())
list.append(self.listToValueFunction.convertToFloat(user_list))
return list
class PerUserPerFeatureDistanceAverageToListFunction(PreferencesToListFunction):
def __init__(self, featureListToValue : ListToValueFunction, product_structure : ProductStructureModel):
self.featureListToValueFunction = featureListToValue
self.product_structure = product_structure
def convertToList(self, preferences : Preferences, toRate : ConfigurationModel) -> List[float]:
user_preferences = preferences.getAllUserPreferences()
feature_list = self.product_structure.get_list_of_features()
user_scores = []
for user_pref in user_preferences:
feature_scores = []
for feature in feature_list:
char_list = feature.get_children_characteristics()
in_to_rate_rating = 0
avg = 0
for char in char_list:
if char.elementId in toRate.configuration:
in_to_rate_rating = user_pref.getRatingByCode(char.elementId).getValue()
avg += user_pref.getRatingByCode(char.elementId).getValue()
if len(char_list) > 0 :
avg = avg / len(char_list)
map_function = MapToPercent(1,-1)
feature_scores.append(map_function.applyToValue(in_to_rate_rating - avg))
user_scores.append(self.featureListToValueFunction.convertToFloat(feature_scores))
return user_scores

View File

@@ -0,0 +1,162 @@
from typing import List
from functools import reduce
import operator
from model.preferences_model import Preferences
from model.configuration_model import ConfigurationModel
from scoring.list_functions import ListToListFunction, ListToValueFunction, Average, Min, Product
from scoring.preferences_functions import PreferencesToListFunction, FlattenPreferencesToListFunction, SimplePerUserToListFunction, PerUserPerFeatureDistanceAverageToListFunction, SimpleSelectedCharacteristicsToListFunction
from scoring.value_functions import ValueToValueFunction, MapToPercent, PowerFunction
from model.product_structure_model import ProductStructureModel
class ScoringFunctionFactory:
@staticmethod
def build_scoring_function(params : List[str]) -> 'ScoringFunction':
pass
class ReduceScoringFunctionFactory(ScoringFunctionFactory):
@staticmethod
def build_scoring_function(params : List[str], product_structure : ProductStructureModel, oper = operator.add) -> 'SumScoring':
list = []
for param in params:
switcher = {
"penalty_ratio" : RatioCharacteristicConfigurationPenalty(product_structure, [PowerFunction(0.5)]),
"penealty_average_weightedFeature_average": WeightedFeaturePenalty(
product_structure,
Average(),
Average()
),
"pref_average_flat": PreferenceScoring(
FlattenPreferencesToListFunction(),
Average()
),
"pref_average_perUser_Average": PreferenceScoring(
SimplePerUserToListFunction(Average()),
Average()
),
"pref_average_simpleSelectedCharacterstics_average": PreferenceScoring(
SimpleSelectedCharacteristicsToListFunction(Average()),
Average()
),
"pref_min_simpleSelectedCharacterstics_average": PreferenceScoring(
SimpleSelectedCharacteristicsToListFunction(Average()),
Min()
),
"pref_product_simpleSelectedCharacterstics_average": PreferenceScoring(
SimpleSelectedCharacteristicsToListFunction(Average()),
Product()
),
"pref_min_perUserPerFeatureDistance_average" : PreferenceScoring(
PerUserPerFeatureDistanceAverageToListFunction(Average(), product_structure),
Min()
),
"pref_average_perUserPerFeatureDistance_average" : PreferenceScoring(
PerUserPerFeatureDistanceAverageToListFunction(Average(), product_structure),
Average()
)
}
value = switcher.get(param, None)
if value != None:
list.append(value)
return ReduceScoring(list, reduce_operator=oper)
class ScoringFunction:
def calc_score(self, currentConfiguration : ConfigurationModel, preferences : Preferences, toRate : ConfigurationModel) -> float:
pass
class PreferenceScoring(ScoringFunction):
def __init__(self, preferenceToList : PreferencesToListFunction, listToValue : ListToValueFunction, listToList: List[ListToListFunction] = [], valueToValue: List[ValueToValueFunction] = []):
self.preferenceToListFunction = preferenceToList
self.listToValueFunction = listToValue
self.listToListFunctions = listToList
self.valueToValueFunctions = valueToValue
def calc_score(self, currentConfiguration : ConfigurationModel, preferences : Preferences, toRate : ConfigurationModel) -> float:
list : List[float] = self.preferenceToListFunction.convertToList(preferences, toRate)
for function in self.listToListFunctions :
list = function.applyToList(list)
value = self.listToValueFunction.convertToFloat(list)
for function in self.valueToValueFunctions :
value = function.applyToValue(value)
return value
class ConfigurationPenalty(ScoringFunction):
def calc_score(self, currentConfiguration : ConfigurationModel, preferences : Preferences, toRate : ConfigurationModel) -> float:
pass
class RatioCharacteristicConfigurationPenalty(ConfigurationPenalty):
def __init__(self, product_structure : ProductStructureModel, valToValFunctions : List[ValueToValueFunction]):
self.product_structure = product_structure
self.valToValFunctions = valToValFunctions
def calc_score(self, currentConfiguration : ConfigurationModel, preferences : Preferences, toRate : ConfigurationModel) -> float:
inCount : float = 0
charCount : float = 0
for code in currentConfiguration.configuration:
if self.product_structure.isCharacteristic(code):
charCount += 1
if code in toRate.configuration:
inCount += 1
if charCount > 0:
res = inCount / charCount
else :
res = 1
for function in self.valToValFunctions:
res = function.applyToValue(res)
return res
class WeightedFeaturePenalty(ScoringFunction):
def __init__(self, product_structure : ProductStructureModel, per_feature_aggregation : ListToValueFunction, per_feature_per_user_aggregation : ListToValueFunction):
self.product_structure = product_structure
self.feature_aggregation = per_feature_aggregation
self.user_aggregation = per_feature_per_user_aggregation
def calc_score(self, currentConfiguration : ConfigurationModel, preferences : Preferences, toRate : ConfigurationModel) -> float:
features = self.product_structure.get_list_of_features()
feature_scores = []
for feature in features:
code_in_current = None
code_in_to_rate = None
for characteristic in feature.children:
if characteristic.elementId in currentConfiguration.configuration :
code_in_current = characteristic.elementId
if characteristic.elementId in toRate.configuration:
code_in_to_rate = characteristic.elementId
users = preferences.getAllUsers()
if code_in_current == None:
break
if code_in_current == code_in_to_rate:
feature_scores.append(1)
else:
user_scores = []
for user in users:
rating_to_rate = preferences.getRatingValueByUserAndCode(user, code_in_to_rate)
rating_current = preferences.getRatingValueByUserAndCode(user, code_in_current)
user_scores.append(rating_to_rate - rating_current)
feature_scores.append(self.user_aggregation.convertToFloat(user_scores))
map_to_percent = MapToPercent(1, -1)
return map_to_percent.applyToValue(self.feature_aggregation.convertToFloat(feature_scores))
class ReduceScoring(ScoringFunction):
def __init__(self, scoringFunctions : List[ScoringFunction], reduce_operator = operator.add):
self.scoringFunctions = scoringFunctions
self.reduce_operator = reduce_operator
def calc_score(self, currentConfiguration : ConfigurationModel, preferences : Preferences, toRate : ConfigurationModel) -> float:
if len(self.scoringFunctions) > 0:
score = reduce(self.reduce_operator, map(lambda x: x.calc_score(currentConfiguration, preferences, toRate), self.scoringFunctions))
return score
else:
return 0

View File

@@ -0,0 +1,39 @@
import math
class ValueToValueFunction:
def applyToValue(self, value : float) -> float :
return value
class MapToPercent(ValueToValueFunction):
def __init__(self, max_val : float, min_val : float):
self.max_val = max_val
self.min_val = min_val
def applyToValue(self, value : float) -> float :
return (value - self.min_val) / abs(self.min_val - self.max_val)
class HighpassFilterFunction(ValueToValueFunction):
def __init__(self, cutoff : float, floor_value=0):
self.cutoff = cutoff
self.floor_value = floor_value
def applyToValue(self, value : float) -> float :
if value < self.cutoff:
return self.floor_value
else:
return value
class LowpassFilterFunction(ValueToValueFunction):
def __init__(self, cutoff : float, ceiling_value=1):
self.cutoff = cutoff
self.ceiling_value = ceiling_value
def applyToValue(self, value : float) -> float :
if value > self.cutoff:
return self.ceiling_value
else:
return value
class PowerFunction(ValueToValueFunction):
def __init__(self, power : float):
self.power = power
def applyToValue(self, value : float) -> float :
return math.pow(value, self.power)

View File

@@ -0,0 +1,28 @@
from model.configuration_model import ConfigurationModel, ConfigurationVariablesModel
import math
import pytest
class TestConfigurationModel:
def test_simple_parsing(self):
data = {
'configuration': ['code1', 'code2'],
'variables': [
{
'code': 'abc',
'value': 1
}
]
}
conf = ConfigurationModel(data)
assert len(conf.configuration) == 2
assert len(conf.variables) == 1
class TestConfigurationVariableModel:
def test_simple_parsing(self):
data = {
'code': 'abc',
'value': 1
}
var = ConfigurationVariablesModel(data)
assert var.code == 'abc'
assert var.value == 1

View File

@@ -0,0 +1,113 @@
from model.preferences_model import Preferences, Rating, UserPreference
import math
import pytest
class TestRating:
def test_range_conversion_minus_one_to_zero(self):
data = {
'code': 'abs',
'value': 0
}
assert math.isclose(0, Rating(data).getValue())
def test_range_conversion_one_to_one(self):
data = {
'code': 'abs',
'value': 1
}
assert math.isclose(1, Rating(data).getValue())
def test_range_conversion_zero_to_half(self):
data = {
'code': 'abs',
'value': 0.5
}
assert math.isclose(0.5, Rating(data).getValue())
def test_value_to_large(self):
with pytest.raises(ValueError):
data = {
'code': 'abs',
'value': 1.1
}
rating = Rating(data)
def test_value_to_small(self):
with pytest.raises(ValueError):
data = {
'code': 'abs',
'value': -0.1
}
rating = Rating(data)
class TestUserPreference:
data = {
'user': "user0",
'ratings':[ {
'code': 'abs',
'value': 0
}, {
'code': '2',
'value': 1
}, {
'code': '3',
'value': 0.5
}
]
}
def test_get_all_ratings(self):
user_pref = UserPreference(self.data)
assert len(user_pref.getAllRatings()) == 3
def test_get_rating_by_code(self):
user_pref = UserPreference(self.data)
rating = user_pref.getRatingByCode('2')
assert rating.code == '2'
assert rating.getValue() == 1
def test_get_rating_by_code_default(self):
user_pref = UserPreference(self.data)
rating = user_pref.getRatingByCode('notFOUND')
assert rating.code == 'notFOUND'
assert rating.getValue() == 0.5
class TestPreferences:
preferences = Preferences({
'preferences': [
{
'user': "user0",
'ratings':[ {
'code': 'in_both',
'value': 0
}, {
'code': 'only_in_one',
'value': 1
}, {
'code': '3',
'value': 0.5
}
]
},
{
'user': "user1",
'ratings':[ {
'code': 'in_both',
'value': 1
}, {
'code': '3',
'value': 1
}
]
}
]
})
def test_get_all_user_preferences(self):
assert len(self.preferences.getAllUserPreferences()) == 2
def test_get_all_rating_by_code(self):
assert len(self.preferences.getAllRatingsByCode('only_in_one')) == 2
assert len(self.preferences.getAllRatingsByCode('in_both')) == 2
def test_get_all_users(self):
assert len(self.preferences.getAllUsers()) == 2
assert "user0" in self.preferences.getAllUsers()
assert "user1" in self.preferences.getAllUsers()
def test_get_rating_by_user_and_code(self):
assert self.preferences.getRatingValueByUserAndCode("user0", "only_in_one") == 1
def test_empty_preferences(self):
assert len(Preferences({ 'preferences' : []}).getAllUsers()) == 0
def test_getIndividual_preferences(self):
assert len(self.preferences.getIndividualPreferences()) == 2

View File

@@ -0,0 +1,100 @@
from model.product_structure_model import ProductStructureModel, ProductStructureElementModel, ProductStructureTypeEnum
class TestProductStructureModelEnum:
def test_string_characteristic(self):
assert ProductStructureTypeEnum['CHARACTERISTIC'] == ProductStructureTypeEnum.CHARACTERISTIC
def test_string_cluster(self):
assert ProductStructureTypeEnum['CLUSTER'] == ProductStructureTypeEnum.CLUSTER
def test_string_variable(self):
assert ProductStructureTypeEnum['VARIABLE'] == ProductStructureTypeEnum.VARIABLE
def test_string_feature(self):
assert ProductStructureTypeEnum['FEATURE'] == ProductStructureTypeEnum.FEATURE
class TestProductStructureElementModel:
def test_get_list_of_all(self):
data = {
'elementId': 'parent',
'name': 'parent_element',
'type': "FEATURE",
'additionalData': [],
'children': [
{
'elementId': 'child',
'name': 'child',
'children': [],
'additionalData': [],
'type': "CHARACTERISTIC"
}
],
}
element = ProductStructureElementModel(data)
assert len(element.get_list_of_all(ProductStructureTypeEnum.CHARACTERISTIC)) == 1
class TestProductStructureModel:
def test_get_list_of_features(self):
data = {
'ProductStructure': [
{
'elementId': 'parent',
'name': 'parent_element',
'type': "FEATURE",
'additionalData': [],
'children': [
{
'elementId': 'child',
'name': 'child',
'children': [],
'additionalData': [],
'type': "CHARACTERISTIC"
}
],
}
]
}
ps_structure = ProductStructureModel(data)
assert len(ps_structure.get_list_of_features()) == 1
def test_get_list_of_characteristics(self):
data = {
'ProductStructure': [
{
'elementId': 'parent',
'name': 'parent_element',
'type': "FEATURE",
'additionalData': [],
'children': [
{
'elementId': 'child',
'name': 'child',
'children': [],
'additionalData': [],
'type': "CHARACTERISTIC"
}
],
}
]
}
ps_structure = ProductStructureModel(data)
assert len(ps_structure.get_list_of_characteristics()) == 1
def test_is_characteristic(self):
data = {
'ProductStructure': [
{
'elementId': 'parent',
'name': 'parent_element',
'type': "FEATURE",
'additionalData': [],
'children': [
{
'elementId': 'child',
'name': 'child',
'children': [],
'additionalData': [],
'type': "CHARACTERISTIC"
}
],
}
]
}
ps_structure = ProductStructureModel(data)
assert ps_structure.isCharacteristic('child') == True
assert ps_structure.isCharacteristic('parent') == False

View File

@@ -0,0 +1,14 @@
from scoring.list_functions import Average, Product
import math
class TestListToValueFunctionAverage:
def test_simple_average(self):
function = Average()
list = [0.0, 1.0]
assert math.isclose(0.5, function.convertToFloat(list))
class TestListToValueFunctionProduct:
def test_simple_product(self):
function = Product()
list = [0.5, 0.5]
assert math.isclose(0.25, function.convertToFloat(list))

View File

@@ -0,0 +1,114 @@
from scoring.preferences_functions import PerUserPerFeatureDistanceAverageToListFunction, SimplePerUserToListFunction, PreferencesToListFunction, FlattenPreferencesToListFunction
from scoring.list_functions import Min
from model.configuration_model import ConfigurationModel
from model.preferences_model import Preferences
from model.product_structure_model import ProductStructureModel
preferences = Preferences({
'preferences': [
{
'user': "user0",
'ratings':[ {
'code': 'A1',
'value': 0
}, {
'code': 'A2',
'value': 1
}, {
'code': 'B1',
'value': 0.5
}
]
},
{
'user': "user1",
'ratings':[ {
'code': 'A1',
'value': 1
}, {
'code': 'B2',
'value': 1
}
]
}
]
})
toRate = ConfigurationModel({
'configuration': ['A1', 'B2'],
'variables': []
})
product_structure = ProductStructureModel({
'ProductStructure': [
{
'elementId': 'A',
'name': 'parent_element A',
'type': "FEATURE",
'additionalData': [],
'children': [
{
'elementId': 'A1',
'name': 'child A1',
'children': [],
'additionalData': [],
'type': "CHARACTERISTIC"
},
{
'elementId': 'A2',
'name': 'child A2',
'children': [],
'additionalData': [],
'type': "CHARACTERISTIC"
}
],
},{
'elementId': 'B',
'name': 'parent_element B',
'type': "FEATURE",
'additionalData': [],
'children': [
{
'elementId': 'B1',
'name': 'child B1',
'children': [],
'additionalData': [],
'type': "CHARACTERISTIC"
},
{
'elementId': 'B2',
'name': 'child B2',
'children': [],
'additionalData': [],
'type': "CHARACTERISTIC"
}
],
},
]
})
def float_lists_same(first, second):
for element in first:
if element not in second:
return False
second.remove(element)
if len(second) != 0:
return False
return True
class TestPreferencesToListFunction:
def test_should_be_empty_list(self):
function = PreferencesToListFunction()
assert len(function.convertToList(preferences, toRate)) == 0
class TestFlattenPreferencesToListFunction():
def test_simple_example(self):
function = FlattenPreferencesToListFunction()
assert float_lists_same([1.0,0.5,0.0,0.0,1.0], function.convertToList(preferences, toRate))
class TestSimplePerUserToListFunction():
def test_simple_example(self):
function = SimplePerUserToListFunction(Min())
assert float_lists_same([0.0, 1.0], function.convertToList(preferences, toRate))
class TestPerUserPerFeatureDistanceAverageToListFunction():
def test_simple_example(self):
function = PerUserPerFeatureDistanceAverageToListFunction(Min(), product_structure)
assert float_lists_same([0.25, 0.625], function.convertToList(preferences, toRate))

View File

@@ -0,0 +1,124 @@
from scoring.scoring_functions import PreferenceScoring, RatioCharacteristicConfigurationPenalty, WeightedFeaturePenalty, ReduceScoring
from scoring.value_functions import ValueToValueFunction
from model.configuration_model import ConfigurationModel
from model.preferences_model import Preferences
from scoring.list_functions import Min, Average
from scoring.preferences_functions import FlattenPreferencesToListFunction
from model.product_structure_model import ProductStructureModel
preferences = Preferences({
'preferences': [
{
'user': "user0",
'ratings':[ {
'code': 'A1',
'value': 0
}, {
'code': 'A2',
'value': 1
}, {
'code': 'B1',
'value': 0.5
}
]
},
{
'user': "user1",
'ratings':[ {
'code': 'A1',
'value': 1
}, {
'code': 'B2',
'value': 1
}
]
}
]
})
currentConfiguration = ConfigurationModel({
'configuration': ['A2', 'B2'],
'variables': []
})
toRate = ConfigurationModel({
'configuration': ['A1', 'B2'],
'variables': []
})
product_structure = ProductStructureModel({
'ProductStructure': [
{
'elementId': 'A',
'name': 'parent_element A',
'type': "FEATURE",
'additionalData': [],
'children': [
{
'elementId': 'A1',
'name': 'child A1',
'children': [],
'additionalData': [],
'type': "CHARACTERISTIC"
},
{
'elementId': 'A2',
'name': 'child A2',
'children': [],
'additionalData': [],
'type': "CHARACTERISTIC"
}
],
},{
'elementId': 'B',
'name': 'parent_element B',
'type': "FEATURE",
'additionalData': [],
'children': [
{
'elementId': 'B1',
'name': 'child B1',
'children': [],
'additionalData': [],
'type': "CHARACTERISTIC"
},
{
'elementId': 'B2',
'name': 'child B2',
'children': [],
'additionalData': [],
'type': "CHARACTERISTIC"
}
],
},
]
})
class TestRatioCharacteristicConfigurationPenalty:
def test_simple_example(self):
function = RatioCharacteristicConfigurationPenalty(product_structure, [ValueToValueFunction()])
assert 0.5 == function.calc_score(currentConfiguration, preferences, toRate)
class TestWeightedFeaturePenalty:
def test_simple_example(self):
function = WeightedFeaturePenalty(product_structure, Min(), Average())
assert 0.375 == function.calc_score(currentConfiguration, preferences, toRate)
class TestReduceScoring:
def test_combined(self):
function = ReduceScoring([
RatioCharacteristicConfigurationPenalty(product_structure, [ValueToValueFunction()]),
WeightedFeaturePenalty(product_structure, Min(), Average())
])
assert 0.875 == function.calc_score(currentConfiguration, preferences, toRate)
def test_none(self):
function = ReduceScoring([])
assert 0 == function.calc_score(currentConfiguration, preferences, toRate)
class TestPreferenceScoring:
def test_simple_example(self):
function = PreferenceScoring(
FlattenPreferencesToListFunction(),
Min()
)
assert 0 == function.calc_score(currentConfiguration, preferences, toRate)

View File

@@ -0,0 +1,44 @@
from scoring.value_functions import MapToPercent, ValueToValueFunction, HighpassFilterFunction, LowpassFilterFunction, PowerFunction
import math
class TestMapToPercent:
def test_range_conversion(self):
function = MapToPercent(-20, -40)
assert math.isclose(1, function.applyToValue(-20))
assert math.isclose(0, function.applyToValue(-40))
assert math.isclose(0.5, function.applyToValue(-30))
class TestValueToValueFunction:
def test_same_value(self):
function = ValueToValueFunction()
assert math.isclose(10, function.applyToValue(10))
class TestHighpassFilterFunction:
def test_higher_value(self):
function = HighpassFilterFunction(0.5)
assert math.isclose(0.8, function.applyToValue(0.8))
def test_lower_value(self):
function = HighpassFilterFunction(0.5)
assert math.isclose(0, function.applyToValue(0.3))
def test_same_value(self):
function = HighpassFilterFunction(0.5)
assert math.isclose(0.5, function.applyToValue(0.5))
class TestLowpassFilterFunction:
def test_higher_value(self):
function = LowpassFilterFunction(0.5)
assert math.isclose(1, function.applyToValue(0.8))
def test_lower_value(self):
function = LowpassFilterFunction(0.5)
assert math.isclose(0.3, function.applyToValue(0.3))
def test_same_value(self):
function = LowpassFilterFunction(0.5)
assert math.isclose(0.5, function.applyToValue(0.5))
class TestPowerFunction:
def test_power_one(self):
function = PowerFunction(10)
assert math.isclose(1, function.applyToValue(1))
def test_power_small_number(self):
function = PowerFunction(4)
assert math.isclose(0.0001, function.applyToValue(0.1))

3
tests/test_simple.py Normal file
View File

@@ -0,0 +1,3 @@
class TestSimple:
def test_simple(self):
assert True

179
vis.py Normal file
View File

@@ -0,0 +1,179 @@
import matplotlib.pyplot as plt
import pandas as pd
import os
def setAxLinesBW(ax):
"""
Take each Line2D in the axes, ax, and convert the line style to be
suitable for black and white viewing.
"""
MARKERSIZE = 3
COLORMAP = {
'#1f77b4': {'marker': None, 'dash': [5,2]},
'#ff7f0e': {'marker': None, 'dash': [3,4]},
'#2ca02c': {'marker': None, 'dash': [1,1]},
'k': {'marker': None, 'dash': (None,None)},
"#d62728": {'marker': None, 'dash': (None,None)},
}
lines_to_adjust = ax.get_lines()
try:
lines_to_adjust += ax.get_legend().get_lines()
except AttributeError:
pass
for line in lines_to_adjust:
origColor = line.get_color()
line.set_color('black')
line.set_dashes(COLORMAP[origColor]['dash'])
line.set_marker(COLORMAP[origColor]['marker'])
line.set_markersize(MARKERSIZE)
def setFigLinesBW(fig):
"""
Take each axes in the figure, and for each line in the axes, make the
line viewable in black and white.
"""
for ax in fig.get_axes():
setAxLinesBW(ax)
def save_figs(folder):
happiness_diff = load_data_frame("{}/data/{}".format(folder, "_happy_increase.csv"))
unhappiness_diff = load_data_frame("{}/data/{}".format(folder, "_unhappy_increase.csv"))
happiness_diff['dictator'] = 0
unhappiness_diff['dictator'] = 0
happiness_total_all = load_data_frame("{}/data/{}".format(folder, "_happy_total_all.csv"))
unhappiness_total_all = load_data_frame("{}/data/{}".format(folder, "_unhappy_total_all.csv"))
column = happiness_total_all.columns[0]
index = happiness_total_all.index[0]
dictator_y_happy = happiness_total_all[column][index] - happiness_diff[column][index]
dictator_y_unhappy = unhappiness_total_all[column][index] - unhappiness_diff[column][index]
figure, axes = new_fig(title="{} Figure 2".format(folder))
x_lim=[0,150]
axes[0].set_title("satisfaction")
axes[0].set_xlim(x_lim)
axes[0].set_xlabel("number of stored configurations")
axes[0].set_ylabel("number of people")
axes[0].axhline(y=dictator_y_happy,linewidth=1, color='k')
happiness_total_all.plot(ax=axes[0])
y_labels_happy_total =axes[0].get_yticks().tolist()
axes[1].set_title("dissatisfaction")
axes[1].set_xlabel("number of stored configurations")
axes[1].set_ylabel("number of people")
axes[1].set_xlim(x_lim)
axes[1].axhline(y=dictator_y_unhappy,linewidth=1, color='k')
unhappiness_total_all.plot(ax=axes[1])
y_labels_unhappy_total =axes[1].get_yticks().tolist()
setFigLinesBW(figure)
#plt.savefig("{}/fig/vis_happy_unhappy_number.pdf".format(folder),format="pdf")
plt.close()
figure, axes = new_fig(title="{} Figure 1".format(folder))
x_lim=[0,150]
left_y_label = "change in number of people"
rigt_y_label = "number of people"
x_label = "number of stored configurations"
axes[0].set_title("satisfaction")
axes[0].set_xlim(x_lim)
axes[0].set_xlabel(x_label)
axes[0].set_ylabel(left_y_label)
#axes[0].axhline(y=0, linewidth=1, color='k')
twin0 = axes[0].twinx()
twin0.set_ylabel(rigt_y_label)
happiness_diff.plot(ax=axes[0])
axes[1].set_title("dissatisfaction")
axes[1].set_xlabel(x_label)
axes[1].set_ylabel(left_y_label)
axes[1].set_xlim(x_lim)
#axes[1].axhline(y=0, linewidth=1, color='k')
twin1 = axes[1].twinx()
twin1.set_ylabel(rigt_y_label)
unhappiness_diff.plot(ax=axes[1])
y_labels_happy = list(map(lambda x: process_label(x, show_plus=True), axes[0].get_yticks().tolist()))
y_labels_unhappy = list(map(lambda x: process_label(x, show_plus=True), axes[1].get_yticks().tolist()))
y_labels_secondary_happy = list(map(lambda x: process_label(x + dictator_y_happy), axes[0].get_yticks().tolist()))
y_labels_secondary_unhappy = list(map(lambda x: process_label(x + dictator_y_unhappy), axes[1].get_yticks().tolist()))
align_labels(axes[0], twin0)
align_labels(axes[1], twin1)
axes[0].set_yticklabels(y_labels_happy)
twin0.set_yticklabels(y_labels_secondary_happy)
axes[1].set_yticklabels(y_labels_unhappy)
twin1.set_yticklabels(y_labels_secondary_unhappy)
setFigLinesBW(figure)
#plt.show()
plt.savefig("{}/fig/vis_happy_unhappy_combined.pdf".format(folder),format="pdf")
plt.close()
def process_label(label, show_plus=False, round_digits = 2):
n_label = round(label, round_digits)
if label > 0 and show_plus:
n_label = "+{}".format(n_label)
else:
n_label = "{}".format(n_label)
return n_label
def align_labels(origin, to_align):
y_low, y_high = origin.get_ylim()
to_align.set_ylim(y_low, y_high)
to_align.set_yticklabels(origin.get_yticks().tolist())
def load_data_frame(path):
frame = pd.read_csv(path, index_col=0).T
frame.index = frame.index.astype(int)
return frame
def new_fig(subplot_row=1, subplot_column=2,aspect_ratio=1.3 ,dpi=300, title="Untitled"):
figure, axes = plt.subplots(subplot_row, subplot_column, sharey=False)
figure.canvas.set_window_title(title)
figure.dpi = dpi
figure.set_figwidth(4 * subplot_column * aspect_ratio)
figure.set_figheight(4 * subplot_row)
plt.subplots_adjust(wspace=0.45)
return figure, axes
def main(dir = "./out"):
for subdir in os.listdir(dir):
path = "{}/{}".format(dir,subdir)
if os.path.isdir(path):
try:
save_figs(path)
print("Generated Figures for: {}".format(subdir))
except OSError as e:
print("Files Not Found in: {}".format(subdir))
if __name__ == "__main__":
main()