Skip to contents

Run the Peter-Clark algorithm for causal discovery using one of several engines.

Usage

pc(engine = c("tetrad", "pcalg", "bnlearn"), test, alpha = 0.05, ...)

Arguments

engine

Character; which engine to use. Must be one of:

"tetrad"

Tetrad Java library.

"pcalg"

pcalg R package.

"bnlearn"

bnlearn R package.

test

Character; name of the conditional‐independence test.

alpha

Numeric; significance level for the CI tests.

...

Additional arguments passed to the chosen engine (e.g. test or algorithm parameters).

Details

For specific details on the supported tests and parameters for each engine, see:

Recommendation

While it is possible to call the function returned directly with a data frame, we recommend using disco(). This provides a consistent interface and handles knowledge integration.

Value

A function that takes a single argument data (a data frame). When called, this function returns a list containing:

  • knowledge A Knowledge object with the background knowledge used in the causal discovery algorithm. See knowledge() for how to construct it.

  • caugi A caugi::caugi object (of class PDAG) representing the learned causal graph from the causal discovery algorithm.

References

Spirtes P, Glymour C, and Scheines R. Causation, Prediction, and Search. MIT Press, 2000.

See also

Examples

data(tpc_example)

#### Using pcalg engine ####
# Recommended path using disco()
pc_pcalg <- pc(engine = "pcalg", test = "fisher_z", alpha = 0.05)
disco(tpc_example, pc_pcalg)
#> <Disco CPDAG: 6 nodes | 6 edges>
#>   nodes: child_x2, child_x1, youth_x4, youth_x3, oldage_x6, oldage_x5
#>   edges: child_x2---child_x1, child_x2-->oldage_x5, child_x2---youth_x4
#>          oldage_x5-->oldage_x6, youth_x3-->oldage_x5, youth_x4-->oldage_x6

# or using pc_pcalg directly
pc_pcalg(tpc_example)
#> <Disco PDAG: 6 nodes | 6 edges>
#>   nodes: child_x2, child_x1, youth_x4, youth_x3, oldage_x6, oldage_x5
#>   edges: child_x2---child_x1, child_x2-->oldage_x5, child_x2---youth_x4
#>          oldage_x5-->oldage_x6, youth_x3-->oldage_x5, youth_x4-->oldage_x6

# With all algorithm arguments specified
pc_pcalg <- pc(
  engine = "pcalg",
  test = "fisher_z",
  alpha = 0.05,
  fixedGaps = NULL,
  fixedEdges = NULL,
  NAdelete = FALSE,
  m.max = 10,
  u2pd = "relaxed",
  skel.method = "original",
  conservative = TRUE,
  maj.rule = FALSE,
  solve.confl = TRUE,
  numCores = 1,
  verbose = FALSE
)

#### Using bnlearn engine with required knowledge ####
kn <- knowledge(
  tpc_example,
  starts_with("child") %-->% starts_with("youth")
)


# Recommended path using disco()
pc_bnlearn <- pc(engine = "bnlearn", test = "fisher_z", alpha = 0.05)
disco(tpc_example, pc_bnlearn, knowledge = kn)
#> Warning: vstructure youth_x4 -> oldage_x6 <- oldage_x5 is not applicable, because one or both arcs are oriented in the opposite direction.
#> <Disco MPDAG: 6 nodes | 9 edges | Knowledge: 4 required>
#> Learned graph:
#>   nodes: child_x2, child_x1, youth_x4, youth_x3, oldage_x6, oldage_x5
#>   edges: child_x1---child_x2, child_x1-->youth_x3, child_x1-->youth_x4
#>          child_x2---oldage_x5, child_x2-->youth_x3, child_x2-->youth_x4
#>          oldage_x5---oldage_x6, oldage_x5-->youth_x3, oldage_x6-->youth_x4
#> Knowledge:
#>   vars: child_x1, child_x2, oldage_x5, oldage_x6, youth_x3, youth_x4
#>   required:
#>     child_x1-->youth_x3 + youth_x4
#>     child_x2-->youth_x3 + youth_x4

# or using pc_bnlearn directly
pc_bnlearn <- pc_bnlearn |> set_knowledge(kn)
pc_bnlearn(tpc_example)
#> Warning: vstructure youth_x4 -> oldage_x6 <- oldage_x5 is not applicable, because one or both arcs are oriented in the opposite direction.
#> <Disco PDAG: 6 nodes | 9 edges>
#>   nodes: child_x2, child_x1, youth_x4, youth_x3, oldage_x6, oldage_x5
#>   edges: child_x1---child_x2, child_x1-->youth_x3, child_x1-->youth_x4
#>          child_x2---oldage_x5, child_x2-->youth_x3, child_x2-->youth_x4
#>          oldage_x5---oldage_x6, oldage_x5-->youth_x3, oldage_x6-->youth_x4


# With all algorithm arguments specified
pc_bnlearn <- pc(
  engine = "bnlearn",
  test = "fisher_z",
  alpha = 0.05,
  max.sx = 2,
  debug = FALSE,
  undirected = TRUE
)

disco(tpc_example, pc_bnlearn)
#> The learned graph is not a valid CPDAG because of conflicting edge orientations, which can happen due to statistical errors in finite samples, violations of faithfulness, or latent confounding; it is reported as PDAG instead.
#> <Disco PDAG: 6 nodes | 6 edges>
#>   nodes: child_x2, child_x1, youth_x4, youth_x3, oldage_x6, oldage_x5
#>   edges: child_x1---child_x2, child_x2---oldage_x5, child_x2---youth_x4
#>          oldage_x5---oldage_x6, oldage_x5---youth_x3, oldage_x6---youth_x4

#### Using tetrad engine with tier knowledge ####
# Requires Tetrad to be installed
if (verify_tetrad()$installed && verify_tetrad()$java_ok) {
  kn <- knowledge(
    tpc_example,
    tier(
      child ~ tidyselect::starts_with("child"),
      youth ~ tidyselect::starts_with("youth"),
      oldage ~ tidyselect::starts_with("oldage")
    )
  )

  # Recommended path using disco()
  pc_tetrad <- pc(engine = "tetrad", test = "fisher_z", alpha = 0.05)
  disco(tpc_example, pc_tetrad, knowledge = kn)

  # or using pc_tetrad directly
  pc_tetrad <- pc_tetrad |> set_knowledge(kn)
  pc_tetrad(tpc_example)
}
#> <Disco UNKNOWN: 6 nodes | 6 edges>
#>   nodes: child_x2, child_x1, youth_x4, youth_x3, oldage_x6, oldage_x5
#>   edges: child_x2---child_x1, child_x2-->oldage_x5, child_x2-->youth_x4
#>          oldage_x5-->oldage_x6, youth_x3-->oldage_x5, youth_x4-->oldage_x6

# With all algorithm arguments specified
if (verify_tetrad()$installed && verify_tetrad()$java_ok) {
  pc_tetrad <- pc(
    engine = "tetrad",
    test = "fisher_z",
    alpha = 0.05,
    conflict_rule = 2,
    depth = 10,
    stable_fas = FALSE,
    guarantee_cpdag = TRUE
  )
  disco(tpc_example, pc_tetrad)
}
#> <Disco CPDAG: 6 nodes | 6 edges>
#>   nodes: child_x2, child_x1, youth_x4, youth_x3, oldage_x6, oldage_x5
#>   edges: child_x2---child_x1, child_x2-->oldage_x5, child_x2---youth_x4
#>          oldage_x5-->oldage_x6, youth_x3-->oldage_x5, youth_x4-->oldage_x6