Package: causalAssembly
causalAssemblyThis repo provides details regarding $\texttt{causalAssembly}$, a causal discovery benchmark data tool based on complex production data.
Theoretical details and information regarding construction are presented in the paper:Göbler, K., Windisch, T., Pychynski, T., Sonntag, S., Roth, M., & Drton, M. (2023). causalAssembly: Generating Realistic Production Data for Benchmarking Causal Discovery. arXiv preprint arXiv:2306.10816.AuthorsKonstantin GoeblerSteffen SonntagMaintainer:Konstantin GoeblerTable of contentsHow to installHow to useHow to testHow to contributeHow to installThe package can be installed as followspip install causalAssemblyHow to useThis is how $\texttt{causalAssembly}$'s functionality may be used. Be sure to read thedocumentationfor more in-depth details regarding available functions and classes.In case you want to train a distributional random forests yourself (seehow to semisynthetsize),
you need an R installation as well as the correspondingdrfR package.
Sampling has first been proposed in[2].Note: For Windows users the python packagerpy2might cause issues.
Please consult theirissue trackeron GitHub.In order to sample semisynthetic data from $\texttt{causalAssembly}$, consider the following example:importpandasaspdfromcausalAssembly.models_dagimportProductionLineGraphfromcausalAssembly.drf_fittingimportfit_drfseed=2023n_select=500assembly_line_data=ProductionLineGraph.get_data()# take subsample for demonstration purposesassembly_line_data=assembly_line_data.sample(n_select,random_state=seed,replace=False)# load in ground truthassembly_line=ProductionLineGraph.get_ground_truth()# fit drf and sample for entire lineassembly_line.drf=fit_drf(assembly_line,data=assembly_line_data)assembly_line_sample=assembly_line.sample_from_drf(size=n_select)# fit drf and sample for station3assembly_line.Station3.drf=fit_drf(assembly_line.Station3,data=assembly_line_data)station3_sample=assembly_line.Station3.sample_from_drf(size=n_select)How to semisynthesizeIn order to generate semisynthetic data for data sources outside the manufacturing
context, the classDAGmay be used. We showcase all necessary steps in the example below using the well-known Sachs [3] dataset.
Note, that thecdtpackage is only needed to get easy access to data and corresponding ground truth.importnetworkxasnxfromcdt.dataimportload_datasetfromcausalAssembly.dagimportDAGfromcausalAssembly.drf_fittingimportfit_drf# load data set and available ground truths_data,s_graph=load_dataset("sachs")# take subset for faster computations_data=s_data.sample(100,random_state=42)print(nx.is_directed_acyclic_graph(s_graph))cycles=nx.find_cycle(s_graph)s_graph.remove_edge(*cycles[0])ifnx.is_directed_acyclic_graph(s_graph):# convert to DAG instancesachs_dag=DAG.from_nx(s_graph)# fit DRF to the conditional distributions implied by# the factorization over <s_graph>sachs_dag.drf=fit_drf(graph=sachs_dag,data=s_data)# sample new data from the trained DRFsdream_benchmark_data=sachs_dag.sample_from_drf(size=50)print(dream_benchmark_data.head())How to generate random production DAGsTheProductionLineGraphclass can further be used to generate completely random DAGs that follow an assembly line logic. Consider the following example:fromcausalAssembly.models_dagimportProductionLineGraphexample_line=ProductionLineGraph()example_line.new_cell(name='Station1')example_line.Station1.add_random_module()example_line.Station1.add_random_module()example_line.new_cell(name='Station2')example_line.Station2.add_random_module(n_nodes=5)example_line.new_cell(name='Station3',is_eol=True)example_line.Station3.add_random_module()example_line.Station3.add_random_module()example_line.connect_cells(forward_probs=[.1])example_line.show()How to generate FCMs$\texttt{causalAssembly}$ also allows creating structural causal models (SCM) or synonymously functional causal models (FCM). In particular, we employ symbolic programming to allow for a seamless interplay between readability and performance. TheFCMclass is completely general and inherits no production data logic. See the example below for construction and usage.importnumpyasnpimportpandasaspdfromsympyimportEq,Symbol,symbolsfromsympy.statsimportGamma,Normal,Uniform,ExponentialfromcausalAssembly.models_fcmimportFCM# declare variables in FCM as symbolsv,w,x,y,z=symbols("v,w,x,y,z")# declare symbol for the variance of a Gaussiandelta=Symbol("delta",positive=True)# Set up FCM# name for the noise terms is required but mainly for readability# it gets evaluated equation-by-equation. Therefore repeating names is completely fine.eq_x=Eq(x,Exponential("source_distribution",0.5))eq_v=Eq(v,Gamma("source_distribution",1,1))eq_y=Eq(y,2*x**2-7*v+Normal("error",0,delta))eq_z=Eq(z,9*y*x*Gamma("noise",0.5,1))eq_w=Eq(w,7*v-z+Uniform("error",left=-0.5,right=0.5))# Collect in a listeq_list=[eq_v,eq_w,eq_x,eq_y,eq_z]# Create instancetest_fcm=FCM()# Input list of equations this automatically# induces the DAG etc.test_fcm.input_fcm(eq_list)# There is an option to use real data for source node samplessource_df=pd.DataFrame({"v":np.random.uniform(low=-0.1,high=0.71,size=10),},columns=["v"],)# Sample from joint distributionprint(test_fcm.sample(size=8,source_df=source_df))test_fcm.show(header="No Intervention")# Multiple hard and soft interventions:test_fcm.intervene_on(nodes_values={z:2,w:Normal("noise",3,1)})print(test_fcm.interventional_sample(size=8,source_df=source_df))# Some plottingtest_fcm.show_mutilated_dag()References[1]Ćevid, D., Michel, L., Näf, J., Bühlmann, P., & Meinshausen, N. (2022). Distributional Random Forests: Heterogeneity Adjustment and Multivariate Distributional Regression. Journal of Machine Learning Research, 23(333), 1-79.[2]Gamella, J.L, Taeb, A., Heinze-Deml, C., & Bühlmann, P. (2022). Characterization and greedy learning of Gaussian structural causal models under unknown noise interventions. arXiv preprint arXiv:2211.14897, 2022.[3]Sachs, K., Perez, O., Pe'er, D., Lauffenburger, D. A., & Nolan, G. P. (2005). Causal protein-signaling networks derived from multiparameter single-cell data. Science, 308(5721), 523-529.How to testIn general we use pytest and the test suite can be executed locally viapython -m pytestHow to contribute?Please feel free to contact one of the authors in case you wish to contribute.Third-Party LicensesRuntime dependenciesNameLicenseTypenumpyBSD-3-Clause LicenseDependencyscipyBSD-3-Clause LicenseDependencypandasBSD 3-Clause LicenseDependencynetworkxBSD-3-Clause LicenseDependencymatplotlibOtherDependencysympyBSD-3-Clause LicenseDependencyrpy2GNU General Public License v2.0DependencyDevelopment dependencyNameLicenseTypemikeBSD-3-Clause LicenseDependencymkdocsBSD-2-Clause LicenseDependencymkdocs-materialMIT LicenseDependencymkdocstrings[python]ISC LicenseDependencyruffMIT LicenseDependencypytestMIT LicenseDependencypip-toolsBSD 3-Clause LicenseDependency