1
0
Fork 0
master
Pietro 3 years ago
parent e32b7c94bc
commit c546e9fcc6
  1. 1
      .gitignore
  2. 20
      PyCTBN/PyCTBN/structure_graph/network_generator.py
  3. 52
      PyCTBN/PyCTBN/structure_graph/trajectory_generator.py
  4. 2
      PyCTBN/PyCTBN/utility/abstract_exporter.py
  5. 8
      PyCTBN/PyCTBN/utility/json_importer.py
  6. 64
      example.py

1
.gitignore vendored

@ -14,3 +14,4 @@ test2.json
test3.json test3.json
result0.png result0.png
example.json example.json
test_time.py

@ -4,8 +4,7 @@ from .conditional_intensity_matrix import ConditionalIntensityMatrix
from .set_of_cims import SetOfCims from .set_of_cims import SetOfCims
import numpy as np import numpy as np
import pandas as pd import pandas as pd
import os import random
import json
class NetworkGenerator(object): class NetworkGenerator(object):
"""Provides the methods to generate a network graph and the CIMs related to it """Provides the methods to generate a network graph and the CIMs related to it
@ -30,15 +29,28 @@ class NetworkGenerator(object):
self._graph = None self._graph = None
self._cims = None self._cims = None
def generate_graph(self, density): def generate_graph(self, density, fixed: bool = False):
"""Generate the edges according to specified density, and then instantiate the NetworkGraph object """Generate the edges according to specified density, and then instantiate the NetworkGraph object
to represent the network to represent the network
:param density: Probability of an edge between two nodes to exist :param density: Probability of an edge between two nodes to exist
:type density: float :type density: float
:param fixed: Specifies whether the required density is mandatory or it's just a probability
:type fixed: bool
""" """
if fixed:
n_edges = density * len(self._labels) * (len(self._labels) - 1)
if not float.is_integer(n_edges):
raise RuntimeError("Invalid Density")
else:
n_edges = int(n_edges)
edges = [(i, j) for i in self._labels for j in self._labels if i != j]
random.shuffle(edges)
edges = edges[:n_edges]
else:
edges = [(i, j) for i in self._labels for j in self._labels if np.random.binomial(1, density) == 1 and i != j]
edges = [(i, j) for i in self._labels for j in self._labels if np.random.binomial(1, density) == 1 and i != j]
s = Structure(self._labels, self._indxs, self._vals, edges, len(self._labels)) s = Structure(self._labels, self._indxs, self._vals, edges, len(self._labels))
self._graph = NetworkGraph(s) self._graph = NetworkGraph(s)
self._graph.add_nodes(s.nodes_labels) self._graph.add_nodes(s.nodes_labels)

@ -25,7 +25,7 @@ class TrajectoryGenerator(object):
:type _generated_trajectory: pandas.DataFrame :type _generated_trajectory: pandas.DataFrame
""" """
def __init__(self, importer: AbstractImporter = None, variables: list = None, dyn_str: list = None, dyn_cims: dict = None): def __init__(self, importer: AbstractImporter = None, variables: pd.DataFrame = None, dyn_str: pd.DataFrame = None, dyn_cims: dict = None):
"""Constructor Method """Constructor Method
It parses and elaborates the data fetched from importer (if defined, otherwise variables, dyn_str and dyn_cims are used) It parses and elaborates the data fetched from importer (if defined, otherwise variables, dyn_str and dyn_cims are used)
in order to make the objects structure more suitable for the forthcoming trajectory generation in order to make the objects structure more suitable for the forthcoming trajectory generation
@ -34,31 +34,35 @@ class TrajectoryGenerator(object):
self._importer = importer self._importer = importer
self._vnames = self._importer._df_variables.iloc[:, 0].to_list() if importer is not None else variables.iloc[:, 0].to_list() self._vnames = self._importer._df_variables.iloc[:, 0].to_list() if importer is not None else variables.iloc[:, 0].to_list()
self._parents = {} self._parents = {}
for v in self._vnames: for v in self._vnames:
if importer is not None: if importer is not None:
self._parents[v] = self._importer._df_structure.where(self._importer._df_structure["To"] == v).dropna()["From"].tolist() self._parents[v] = self._importer._df_structure.where(self._importer._df_structure["To"] == v).dropna()["From"].tolist()
else: else:
self._parents[v] = dyn_str.where(dyn_str["To"] == v).dropna()["From"].tolist() self._parents[v] = dyn_str.where(dyn_str["To"] == v).dropna()["From"].tolist()
self._cims = {} self._cims = {}
sampled_cims = self._importer._raw_data[0]["dyn.cims"] if importer is not None else dyn_cims if importer is not None:
for v in sampled_cims.keys(): sampled_cims = self._importer._cims
p_combs = []
v_cims = [] for v in sampled_cims.keys():
for comb in sampled_cims[v].keys(): p_combs = []
p_combs.append(np.array(re.findall(r"=(\d)", comb)).astype("int")) v_cims = []
cim = pd.DataFrame(sampled_cims[v][comb]).to_numpy() for comb in sampled_cims[v].keys():
v_cims.append(ConditionalIntensityMatrix(cim = cim)) p_combs.append(np.array(re.findall(r"=(\d)", comb)).astype("int"))
cim = pd.DataFrame(sampled_cims[v][comb]).to_numpy()
if importer is not None: v_cims.append(ConditionalIntensityMatrix(cim = cim))
sof = SetOfCims(node_id = v, parents_states_number = [self._importer._df_variables.where(self._importer._df_variables["Name"] == p)["Value"] for p in self._parents[v]],
node_states_number = self._importer._df_variables.where(self._importer._df_variables["Name"] == v)["Value"], p_combs = np.array(p_combs), cims = v_cims) if importer is not None:
else: sof = SetOfCims(node_id = v, parents_states_number = [self._importer._df_variables.where(self._importer._df_variables["Name"] == p)["Value"] for p in self._parents[v]],
sof = SetOfCims(node_id = v, parents_states_number = [variables.where(variables["Name"] == p)["Value"] for p in self._parents[v]], node_states_number = self._importer._df_variables.where(self._importer._df_variables["Name"] == v)["Value"], p_combs = np.array(p_combs), cims = v_cims)
node_states_number = variables.where(variables["Name"] == v)["Value"], p_combs = np.array(p_combs), cims = v_cims) else:
self._cims[v] = sof sof = SetOfCims(node_id = v, parents_states_number = [variables.where(variables["Name"] == p)["Value"] for p in self._parents[v]],
node_states_number = variables.where(variables["Name"] == v)["Value"], p_combs = np.array(p_combs), cims = v_cims)
self._cims[v] = sof
else:
self._cims = dyn_cims
def CTBN_Sample(self, t_end = -1, max_tr = -1): def CTBN_Sample(self, t_end = -1, max_tr = -1):
"""This method implements the generation of a trajectory, basing on the network structure and """This method implements the generation of a trajectory, basing on the network structure and
@ -97,7 +101,6 @@ class TrajectoryGenerator(object):
time[i] = t + random.exponential(scale = param) time[i] = t + random.exponential(scale = param)
# next = index of the variable that will transition first
next = time.argmin() next = time.argmin()
t = time[next] t = time[next]
@ -173,11 +176,4 @@ class TrajectoryGenerator(object):
for p in processes: for p in processes:
p.join() p.join()
return trajectories return trajectories
def to_json(self):
"""Convert the last generated trajectory from pandas.DataFrame object type to JSON format
(suitable to do input/output of the trajectory with file)
"""
return json.loads(self._generated_trajectory.to_json(orient="records"))

@ -4,7 +4,7 @@ import os
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
class AbstractExporter(ABC): class AbstractExporter(ABC):
"""Provides the methods to save in json format a network information """Abstract class that exposes the methods to save in json format a network information
along with one or more trajectories generated basing on it along with one or more trajectories generated basing on it
:param _variables: Dataframe containing the nodes labels and cardinalities :param _variables: Dataframe containing the nodes labels and cardinalities

@ -30,8 +30,8 @@ class JsonImporter(AbstractImporter):
:type _raw_data: List :type _raw_data: List
""" """
def __init__(self, file_path: str, samples_label: str, structure_label: str, variables_label: str, time_key: str, def __init__(self, file_path: str, samples_label: str, structure_label: str, variables_label: str, time_key: str,
variables_key: str): variables_key: str, cims_label: str = None):
"""Constructor method """Constructor method
.. note:: .. note::
@ -42,6 +42,7 @@ class JsonImporter(AbstractImporter):
self._samples_label = samples_label self._samples_label = samples_label
self._structure_label = structure_label self._structure_label = structure_label
self._variables_label = variables_label self._variables_label = variables_label
self._cims_label = cims_label
self._time_key = time_key self._time_key = time_key
self._variables_key = variables_key self._variables_key = variables_key
self._df_samples_list = None self._df_samples_list = None
@ -63,6 +64,9 @@ class JsonImporter(AbstractImporter):
self._df_structure = self.import_structure(self._raw_data) self._df_structure = self.import_structure(self._raw_data)
self._df_variables = self.import_variables(self._raw_data) self._df_variables = self.import_variables(self._raw_data)
if self._cims_label != None:
self._cims = self._raw_data[indx][self._cims_label]
def import_trajectories(self, raw_data: typing.List) -> typing.List: def import_trajectories(self, raw_data: typing.List) -> typing.List:
"""Imports the trajectories from the list of dicts ``raw_data``. """Imports the trajectories from the list of dicts ``raw_data``.

@ -6,40 +6,36 @@ from PyCTBN.PyCTBN.structure_graph.structure import Structure
from PyCTBN.PyCTBN.structure_graph.sample_path import SamplePath from PyCTBN.PyCTBN.structure_graph.sample_path import SamplePath
from PyCTBN.PyCTBN.estimators.structure_constraint_based_estimator import StructureConstraintBasedEstimator from PyCTBN.PyCTBN.estimators.structure_constraint_based_estimator import StructureConstraintBasedEstimator
""" def main():
if __name__ == "__main__": # Network Generation
trajectories = tg.multi_trajectory(t_ends = [100, 100, 100]) labels = ["X", "Y", "Z"]
""" card = 3
vals = [card for l in labels]
cim_min = 1
cim_max = 3
ng = NetworkGenerator(labels, vals)
ng.generate_graph(0.3)
ng.generate_cims(cim_min, cim_max)
# Network Generation # Trajectory Generation
labels = ["X", "Y", "Z"] e1 = JsonExporter(ng.variables, ng.dyn_str, ng.cims)
card = 3 tg = TrajectoryGenerator(variables = ng.variables, dyn_str = ng.dyn_str, dyn_cims = ng.cims)
vals = [card for l in labels] sigma = tg.CTBN_Sample(max_tr = 30000)
cim_min = 1 e1.add_trajectory(sigma)
cim_max = 3 e1.out_file("example.json")
ng = NetworkGenerator(labels, vals)
ng.generate_graph(0.3)
ng.generate_cims(cim_min, cim_max)
# Trajectory Generation # Network Estimation (Constraint Based)
print(ng.dyn_str) importer = JsonImporter(file_path = "example.json", samples_label = "samples",
e1 = JsonExporter(ng.variables, ng.dyn_str, ng.cims) structure_label = "dyn.str", variables_label = "variables",
tg = TrajectoryGenerator(variables = ng.variables, dyn_str = ng.dyn_str, dyn_cims = e1.cims_to_json()) cims_label = "dyn.cims", time_key = "Time",
sigma = tg.CTBN_Sample(max_tr = 30000) variables_key = "Name")
e1.add_trajectory(sigma) importer.import_data(0)
e1.out_file("example.json") s1 = SamplePath(importer=importer)
s1.build_trajectories()
s1.build_structure()
se1 = StructureConstraintBasedEstimator(sample_path=s1, exp_test_alfa=0.1, chi_test_alfa=0.1,
known_edges=[], thumb_threshold=25)
edges = se1.estimate_structure(True)
# Network Estimation (Constraint Based) if __name__ == "__main__":
importer = JsonImporter(file_path="example.json", samples_label='samples', main()
structure_label='dyn.str', variables_label='variables',
time_key='Time', variables_key='Name')
importer.import_data(0)
s1 = SamplePath(importer=importer)
s1.build_trajectories()
s1.build_structure()
se1 = StructureConstraintBasedEstimator(sample_path=s1, exp_test_alfa=0.1, chi_test_alfa=0.1,
known_edges=[], thumb_threshold=25)
edges = se1.estimate_structure(True)
# se1.save_plot_estimated_structure_graph('./result1.png')
print(se1.adjacency_matrix())
print(edges)