rasta/rasta_data_manipulation/rasta_triturage/utils.py
Jean-Marie Mineau cd1e91bb99
first commit
2023-11-16 14:30:24 +01:00

185 lines
5 KiB
Python

"""
Utils.
"""
import matplotlib.pyplot as plt # type: ignore
import numpy as np
from slugify import slugify # type: ignore
from typing import Any, Callable, Optional
from pathlib import Path
import sqlite3
DENSE_DASH = (0, (5, 1))
DENSE_DOT = (0, (1, 3))
MARKERS = {
"adagio": ".",
"amandroid": "o",
"anadroid": "X",
"androguard": "+",
"androguard_dad": "v",
"apparecium": "d",
"blueseal": "^",
"dialdroid": "<",
"didfail": ">",
"droidsafe": r"$\circ$",
"flowdroid": r"$\boxplus$",
"gator": r"$\otimes$",
"ic3": "1",
"ic3_fork": "s",
"iccta": "P",
"mallodroid": r"$\divideontimes$",
"perfchecker": "*",
"redexer": "x",
"saaf": "D",
"wognsen_et_al": r"$\rtimes$",
}
COLORS = {
"didfail": "#1f77b4",
"adagio": "#ff7f0e",
"iccta": "#2ca02c",
"androguard": "#d62728",
"gator": "#9467bd",
"mallodroid": "#8c564b",
"dialdroid": "#e377c2",
"androguard_dad": "#7f7f7f",
"wognsen_et_al": "#bcbd22",
"perfchecker": "#17becf",
"amandroid": "#1f77b4",
"ic3": "#ff7f0e",
"apparecium": "#2ca02c",
"blueseal": "#d62728",
"droidsafe": "#9467bd",
"redexer": "#8c564b",
"anadroid": "#e377c2",
"saaf": "#7f7f7f",
"ic3_fork": "#bcbd22",
"flowdroid": "#17becf",
"adagio": "#1f77b4",
"androguard": "#ff7f0e",
"mallodroid": "#2ca02c",
"androguard_dad": "#d62728",
"wognsen_et_al": "#9467bd",
"amandroid": "#8c564b",
"apparecium": "#e377c2",
"redexer": "#7f7f7f",
}
def get_list_tools(db: Path) -> list[str]:
"""Get the list of tool found in the database."""
with sqlite3.connect(db) as con:
cur = con.cursor()
tools = cur.execute("SELECT DISTINCT tool_name FROM exec;")
return [tool[0] for tool in tools]
def radar_chart(
axes: list[str],
values: list[list[Any]],
labels: list[str],
title: str,
interactive: bool,
image_path: Path | None,
):
plt.rc("grid", linewidth=1, linestyle="-")
plt.rc("xtick", labelsize=15)
plt.rc("ytick", labelsize=15)
angles = np.linspace(0, 2 * np.pi, len(axes), endpoint=False)
angles = np.concatenate((angles, [angles[0]])) # type: ignore
fig = plt.figure(figsize=(8, 8))
ax = fig.add_subplot(111, polar=True)
for label, vals in zip(labels, values):
vals = vals + [vals[0]]
ax.plot(angles, vals, label=label, marker=MARKERS.get(label, "."))
ax.fill(angles, vals, alpha=0.25)
ax.set_thetagrids(angles[:-1] * 180 / np.pi, axes)
ax.set_ylim(bottom=0)
ax.grid(True)
ncol = min(5, len(labels))
ax.legend(
loc="lower left",
bbox_to_anchor=(0.0, -0.2, ncol * 1.0 / 5, 0.102),
ncol=ncol,
mode="expand",
borderaxespad=0.0,
fancybox=True,
shadow=True,
fontsize="xx-small",
)
render(title, interactive, image_path)
def render(
title: str, interactive: bool, image_path: Path | None, tight_layout: bool = True
):
"""Render the figure. If `interactive`, display if, if `image_path`, save it."""
# plt.title(title)
if tight_layout:
plt.tight_layout()
if image_path is not None:
if not image_path.exists():
image_path.mkdir(parents=True, exist_ok=True)
plt.savefig(image_path / (slugify(title) + ".pdf"), format="pdf")
if interactive:
plt.show()
plt.close()
def mean(field: str) -> Callable[[list[Any]], float]:
def compute_mean(data: list[Any]) -> float:
s = 0
n = 0
for e in data:
n += 1
s += e[field]
return 0.0 if n == 0 else s / n
return compute_mean
def median(field: str) -> Callable[[list[Any]], float]:
def compute_median(data: list[Any]) -> float:
l = [e[field] for e in data if e[field] is not None]
l.sort()
if not l:
return 0.0
return l[len(l) // 2]
return compute_median
def plot_generic(
data: list[list[tuple[Any, Any]]],
meta: list[tuple[str, Any, Any, str]],
x_label: str,
y_label: str,
title: str,
ylim: Optional[tuple[int, int]] = None,
interactive: bool = True,
image_path: Path | None = None,
):
"""Plot a list of curve represented by list[(x, y)]. meta is the list of (label, linestyle)
for each plot.
"""
plt.figure(figsize=(16, 9), dpi=80)
for i, plot in enumerate(data):
label, linestyle, marker, color = meta[i]
plot.sort(key=lambda p: p[0])
x_values = np.array([x for (x, _) in plot])
y_values = np.array([y for (_, y) in plot])
plt.plot(
x_values[~np.isnan(y_values)],
y_values[~np.isnan(y_values)],
label=label,
marker=marker,
color=color,
linestyle=linestyle,
)
if ylim is not None:
plt.ylim(ylim)
plt.legend(loc="upper center", ncol=4, bbox_to_anchor=(0.5, -0.1))
plt.xlabel(x_label)
plt.ylabel(y_label)
render(title, interactive, image_path)