Title: | R interface to Netica(R) Bayesian Network Engine |
---|---|
Description: | This provides an R interface to the Netica (http://norsys.com/) Bayesian network library API. |
Authors: | Russell Almond |
Maintainer: | Russell Almond <[email protected]> |
License: | file LICENSE |
Version: | 0.10-3 |
Built: | 2024-11-02 04:43:04 UTC |
Source: | https://github.com/ralmond/RNetica |
This provides an R interface to the Netica (http://norsys.com/) Bayesian network library API.
The DESCRIPTION file:
Package: | RNetica |
Version: | 0.10-3 |
Date: | 2024/06/20 |
Title: | R interface to Netica(R) Bayesian Network Engine |
Author: | Russell Almond |
Maintainer: | Russell Almond <[email protected]> |
Authors@R: | person(given = "Russell", family = "Almond", role = c("aut", "cre"), email = "[email protected]", comment = c(ORCID = "0000-0002-8876-9337")) |
Depends: | R (>= 3.0), methods, utils, CPTtools (>= 0.5-3) |
Imports: | grDevices, R.utils |
Suggests: | knitr, rmarkdown, tidyr, futile.logger |
VignetteBuilder: | knitr |
Description: | This provides an R interface to the Netica (http://norsys.com/) Bayesian network library API. |
License: | file LICENSE |
URL: | https://pluto.coe.fsu.edu/RNetica |
Collate: | Session.R Networks.R Node.R Edges.R Inference.R Cases.R Experience.R Continuous.R Random.R LoadFuns.R Tester.R |
Biarch: | true |
Support: | c( 'Bill & Melinda Gates Foundation grant "Games as Learning/Assessment: Stealth Assessment" (#0PP1035331, Val Shute, PI)', 'National Science Foundation grant "DIP: Game-based Assessment and Support of STEM-related Competencies" (#1628937, Val Shute, PI)', 'National Science Foundation grant "Mathematical Learning via Architectural Design and Modeling Using E-Rebuild." (\#1720533, Fengfeng Ke, PI)', 'Institute of Educational Statistics Grant: "Exploring adaptive cognitive and affective learning support for next-generation STEM learning games." (#R305A170376-20, Val Shute and Russell Almond, PIs') |
Repository: | https://ralmond.r-universe.dev |
RemoteUrl: | https://github.com/ralmond/RNetica |
RemoteRef: | HEAD |
RemoteSha: | e9c01577ff3f6104b5ace2129ef2bc9ca803381f |
This package provides an R interface to the Netica, in particular, it binds many of the functions in the Netica C API into the R language. RNetica can create and modify networks, enter evidence and extract the conditional probabilities from a Netica network.
While RNetica (the combination of R and C code that connects R and Netica) is free software, as is R, Netica is a commercial product. Users of RNetica will need to purchase a Netica API license key (which is different from the GUI license key) from Norsys(R) (http://www.norsys.com/).
Once you have a license key, you can use it in one of three ways.
The currently (RNetica 0.5 and later) recommended way of using it is
to create a Netica Session object that contains it:
DefaultNeticaSession <-
NeticaSession(LicenseKey="License Key from Norsys")
. This will store
the key in a NeticaSession
object. The special
variable DefaultNeticaSession
is used as a default for every
function requiring a session argument, so can be used to skip the need
for explicitly stating the session argument.
Two other mechanisms continue to be supported for backwards
compatibility. First, the license key can be used as an argument to
the function StartNetica()
. This will create a session
and store it in NeticaDefaultSession
. If the variable
NeticaLicenseKey
in the R top-level environment is set before
the call to library(RNetica)
, StartNetica()
will pick up
the license key from that location.
Without the license key, the Netica shared library will be restricted
to a student/demonstration mode with limited functionality. Note that
all of the example code (and hence R CMD check RNetica
) can be
run using the limited version.
Note that the NeticaSession
object stores the
complete Netica license key. Do not share dumps of the session object
(including the .RData
file containing
DefaultNeticaSession
) with any third-party.
Index of help topics:
AbsorbNodes Delete a Netica nodes in a way that maintains the connectivity. AddLink Adds or removes a link between two nodes in a Netica network. AdjoinNetwork Links an evidence model network to a system model network. CalcNodeState Calculates the state of a node based on logical functions or formulae CaseFileDelimiter Gets or sets special characters for case files. CaseFileStream A stream of cases for reading/writing Netica findings to a file CaseMemoryStream A stream of cases for reading/writing Netica from memory CaseStream-class Class '"CaseStream"' CliqueNode-class Class '"CliqueNode"' CompileNetwork Builds the junction tree for a Netica Network CopyNetworks Makes copies of Netica networks. CopyNodes Copies or duplicates nodes in a Netica network. CreateNetwork Creates (destroys) a new Netica network. DeleteNodeTable Deletes the conditional probability table of a Netica node. EliminationOrder Retrieves or sets the elimination order used in compiling a Netica network. EnterFindings Enters findings for multiple nodes in a Netica network. EnterGaussianFinding Enter a numeric finding with uncertainty EnterIntervalFinding Enter finding of value within an interval EnterNegativeFinding Sets findings for a Netica node to a list of ruled out values. Extract.NeticaNode Extracts portions of the conditional probability table of a Netica node. FadeCPT Fades a Netica Conditional Probability Table FileCaseStream-class Class '"FileCaseStream"' FindingsProbability Finds the probability of the findings entered into a Netica network. GenerateRandomCase Generates random cases for nodes in a Netica network GetNamedNetworks Finds a Netica network (if it exists) for the name. GetNetworkAutoUpdate Turns Netica automatic updating on or off for a network. GetNthNetwork Fetch a Netica network by its position in the Netica list. HasNodeTable Tests to see if a Netica node has a conditional probability table. IDname Tests to see if a string is a valid as a Netica Identifier. IsNodeDeterministic Determines if a node in a Netica Network is deterministic or not. JointProbability Calculates the joint probability over several network nodes. JunctionTreeReport Produces a report about the junction tree from a compiled Netica network. LearnCPTs Learn Conditional Probability Tables with Missing Data. LearnCases Learn Conditional Probability Tables from a Netica Case Stream LearnFindings Learn Netica conditional probabilities from findings. MakeCliqueNode Forces a collection of nodes in a Netica network to be in the same clique. MemoryCaseStream-class Class '"MemoryCaseStream"' MemoryStreamContents Access the contents of a MemoryCaseStream MostProbableConfig Finds the configuration of the nodes most likely to have lead to observed findings. MutualInfo Calculates strength of relationship between two nodes in a network NeticaBN An object referencing a Bayesian network in Netica. NeticaBN-class Class '"NeticaBN"' NeticaNode An object referencing a node in a Netica Bayesian network. NeticaNode-class Class '"NeticaNode"' NeticaRNG-class Class '"NeticaRNG"' NeticaSession Creates a connection between R and Netica NeticaSession-class Class '"NeticaSession"' NeticaVersion Fetches the version number of Netica. NetworkFindNode Finds nodes in a Netica network. NetworkFootprint Returns a list of names of unconnected edges. NetworkName Gets or Sets the name of a Netica network. NetworkNodeSetColor Returns or sets a display colour to use with a Netica node.b NetworkNodeSets Returns a list of node sets associated with a Netica network. NetworkNodesInSet Returns a list of node labeled with the given node set in a Netica Network. NetworkSetPriority Changes the priority order of the node sets. NetworkSetRNG Sets a random number generator associates with the network. NetworkTester-class Class '"NetworkTester"' NetworkTitle Gets the title or comments associated with a Netica network. NetworkUndo Undoes (redoes) a Netica operation on a network. NetworkUserField Gets user definable fields associated with a Netica network. NewDiscreteNode Creates (or destroys) a node in a Netica Bayesian network. NewNeticaRNG Creates a Netica Random Number Generator NodeBeliefs Returns the current marginal probability distribution associated with a node in a Netica network. NodeChildren Returns a list of the children of a node in a Netica network. NodeEquation Gets or sets the equation Netica uses to calculate the CPT for a node NodeExpectedUtils Calculates expected utility for each value of a decision node NodeExpectedValue Calculates expected value for a numeric node NodeExperience Gets or sets the amount of experience associated with a node. NodeFinding Returns of sets the observed value associated with a Netica node. NodeInputNames Associates names with incoming edges on a Netica node. NodeKind Gets or changes the kind of a node in a Netica network. NodeLevels Accesses the levels associated with a Netica node. NodeLikelihood Returns or sets the virtual evidence associated with a Netica node. NodeName Gets or sets name of a Netica node. NodeNet Finds which Netica network a node comes from. NodeParents Gets or sets the parents of a node in a Netica network. NodeProbs Gets or sets the conditional probability table associated with a Netica node. NodeSets Lists or changes the node sets associated with a Netica node. NodeStateTitles Accessors for the titles and comments associated with states of Netica nodes. NodeStates Accessor for states of a Netica node. NodeTitle Gets the title or Description associated with a Netica node. NodeUserField Gets user definable fields associated with a Netica node. NodeValue Sets the numeric value of a continuous node NodeVisPos Gets, sets the visual position of the node on the Netica display. NodeVisStyle Gets/sets the nodes visual appearance in Netica. OpenCaseStream Functions for manipulating Netica case streams ParentStates Returns a list of the names of the states of the parents of a Netica node. RNetica-package R interface to Netica(R) Bayesian Network Engine ReadFindings Retrieves a record from a Netica Case Stream RetractNodeFinding Clears any findings for a Netica node or network. ReverseLink Reverses a link in a Netica network. StartNetica Starting and stopping the Netica shared library. WithOpenCaseStream Evaluate an expression and then close the Netica Case Stream. WriteFindings Appends the current findings to a Netica case file. WriteNetworks Reads or writes a Netica network from a file. dgetFromString Serializes an R object to a string is.NodeRelated Computes topological proprieties of a 'Netica' network. is.active Check to see if a Netica network or node object is still valid. is.discrete Determines whether a Netica node is discrete or continuous. testNetwork Performs a classification recovery test on a network. testerConfusion Accessor methods for class '"NetworkTester"' woe Calculates the weight of evidence for a hypothesis write.CaseFile Read or write data frame in Netica Case File format.
Further information is available in the following vignettes:
ConstructingNetworks |
Constructing Networks (source) |
Inference |
Inference (source) |
LicenseKeys |
Netica License Keys (source) |
RNeticaInternals |
RNetica Internals (source) |
Netica exists in both as a stand alone graphical tool for building and manipulating Bayesian networks (the Netica GUI) and as a shared library for manipulating Bayesian networks (the Netica API). The RNetica package binds the API version of Netica to a series of R functions which do much of the work of manipulating the network. The file format for the GUI and API version of Netica is identical, so analysts can easily move back and forth between the two. Note that the RNetica environment is separate from other Netica environments that may be created using the Netica GUI (or API invoked from a different program); RNetica can only manipulate the networks that are currently loaded into its environment.
There are five objects which provide a handle for objects in the Netica session. These are:
NeticaSession
This is a container for the
overall Netica session. It is referenced when creating other Netica
objects (NeticaBN
s,
CaseStream
s and
NeticaRNG
) and contains the license key needed
to activate Netica. Its field $nets
is an environment which
contains references to all of the networks which have been associate
with this session.
NeticaBN
This is a handle for a network
object. NeticaNode
objects are created within
a network, and the $nodes
field is an environment which
contains node references, at least for those nodes which have been
referenced in R code. Networks must have unique names within a
session.
NeticaNode
This is a handle for a particular Netica node. Nodes must have unique names within a network. Many inference functions are done based on nodes.
CaseStream
This is a stream of Netica case
data, values for particular nodes. There are two subclasses:
FileCaseStream
and
MemoryCaseStream
. As of version 5.04 of the
Netica API, there are some issues with
MemoryCaseStream
s, so the
FileCaseStream
s should be used instead.
NeticaRNG
This a random number generator used by Netica for generating random cases.
All of these follow the envRefClass
protocol. In
particular, their fields are referenced using ‘$’. Also, all of
them have a method $isActive
(which is called from the
generic function is.active
) which determines whether or not
the pointer to the Netica object currently exists or not. Calling
stopSession
will render all Netica objects inactive.
In particular, when quitting and restarting R, the pointers will all be initialised to null, and all of the session, node and network objects will become inactive. Some examples of how to restart an RNetica session are provided below.
To connect R to Netica, it is necessary to create and start a
NeticaSession
. This is done by first calling the
constructor NeticaSession()
and then calling the function
startSession(session)
. If you have purchased a
Netica license key from Norsys, this can be passed to the constructor
with the argument LicenseKey
given the value of the license key
as a string. Note that the session object can be saved in the
workspace, so that it can be used in future R session (it does not need
to be recreated, but it must be restarted with a call to
startSession
). If it is saved to DefaultNeticaSession
,
this value will be used as a default by all of the functions that use
the session as an argument.
Note that this is a change from how RNetica operated prior to
version 0.5. In older versions of RNetica, the session pointer was
held inside of the C code, and the function
StartNetica()
was invoked automatically when
the RNetica
package was attached. Nowt this needs to be
done manually through a call to startSession
.
The function getDefaultSession()
emulates the behaviour of
the previous version of RNetica. It is the default value for all of
the functions which require a session argument. When invoked, it looks
for an object call DefaultNeticaSession
in the global
environment. If that exists, it is used, if not, a new
NeticaSession
is created. If the new session is
created, it looks for a variable NeticaLicenseKey
in the global
environment. If that is present, it will use this as a license key.
Finally, if the DefaultNeticaSession
is not active, it will start
it.
Note that it is almost certainly a mistake to have two sessions open at
the same time. Users should either set the DefaultNeticaSession
,
and use the default, or always explicitly pass the session argument to
functions that need it.
The following functions take a session argument:
CaseFileDelimiter
, CaseFileMissingCode
,
CaseFileStream
, CaseMemoryStream
,
ClearAllErrors
, CreateNetwork
,
GetNthNetwork
, GetNamedNetworks
,
NeticaVersion
, ReadNetworks
,
ReportErrors
, StartNetica
,
StopNetica
, startSession
,
and stopSession
.
NeticaBN
objects are created through one of three
functions: CreateNetwork()
,
ReadNetworks()
and CopyNetworks
. The
first two both require a session argument, while the third uses the
session from its net argument. When a network is created it is
added as a symbol (using its name) to the $nets
field of the
session. It can then be referenced using
session$nets$netname
or
session$nets[["netname"]]
. The field
$Session
of the NeticaBN
points to the
NeticaSession
object in which the network was
created.
Note that session$nets
cache may contain inactive network
objects for one of two reasons: (1) it is a deleted network object,
or (2) this is a session which has been restored from a file, and
the Netica pointers have not been reconnected. In particular,
quitting R will always deactivate the network.
For networks, the simplest solution is to save each network to a file
using WriteNetworks()
. If a
NeticaBN
object net is used in either a
net <- ReadNetworks()
or
WriteNetworks(net)
call, then the R object will be
badged with the name of the last used filename. Thus, after saving
and restoring a R session, the expression net <-
ReadNetworks(net)
will recreate net as an object
pointing to a new network that is identical to the last saved version.
NeticaNode
objects are created through
NewDiscreteNode()
or NewContinuousNode()
,
or retrieved from the network using NetworkFindNode()
,
NetworkAllNodes()
, NetworkNodesInSet()
, or
one of a variety of other functions that return nodes. When a node
is created it is added as a symbol (using its name) to the
$node
field of the network. It can then be referenced using
net$nodesnodename
or
net$nodes[["nodename"]]
.
Note that if more than one network is loaded they may have identically
named nodes that are not identical. For example, net1 and
net2 may both have a node named “Proficiency”. If the R
variable Proficiency is bound to the
NeticaNode
object corresponding to the variable
“Proficiency” in net1, it can only be used to access the
instance of that variable in net1, not the one in net2.
Note that the NeticaNode
object is created when
then node is first references in R. In particular, this means when a
network is loaded through a call to ReadNetworks
, the R
objects for the corresponding Netica nodes are not immediately
created. The function NetworkAllNodes()
returns a list
of all nodes in the network, and as a side effect, creates
NeticaNode
object for all of the nodes found in
the network. If the network has many nodes, it may be more efficient
to just create R objects for the ones which are used. In this case
the functions NetworkFindNode()
, and
NetworkNodesInSet
are useful for finding (and creating R
objects for) a subset of nodes.
The following procedure can be used to save and restore a Netica network across sessions. In the first session:
DefaultNeticaSession <- NeticaSession() startSession(DefaultNeticaSession) net <- CreateNetwork("myNet",DefaultNeticaSession) # Work on the network. WriteNetworks(net,"myNet.dne") q("yes")
The variables DefaultNeticaSession
and net
will be saved
in .Rdata
. Then in the next R session
startSession(DefaultNeticaSession) net <- ReadNetworks(net,DefaultNeticaSession) net.nodes <- NetworkAllNodes(net)
This will read net from the place it was last saved. It will
also create R objects for all of the nodes in net. This can now
be access through net$nodes
or the variable
net.nodes
.
Operations with Bayesian networks generally proceed in two phases: Building network, and conducting inference. This section describes the most commonly used options for building networks. The following section describes the most commonly used options for inference.
First, the function CreateNetwork()
is used to create an
empty network. Multiple networks can be open within the RNetica
environment, but each must have a unique name. Names must conform to
Netica's IDname
rules.
Nodes can be added to a network with the functions
NewDiscreteNode()
and
NewContinuousNode()
. Note that Netica makes an internal
distinction between these two types of nodes and a node cannot be
changed from one type to another. Nodes must all have a unique
(within the network) name which must conform to the
IDname
rules.
Edges between nodes are created using the
AddLink(parent,child)
function. This forms
a directed graph which must be acyclic (that is it must not be
possible to follow a path along the direction of the arrows and return
to the starting place). The function
NodeParents(child)
returns the current set of
parents for the node child (nodes which have edges pointing
towards child). NodeParents(child)
may be set,
which serves several purposes. First, it allows connections to be
added and removed. Second, setting one of the parent locations to
NULL
produces a special Stub node, which serves as a
placeholder for a later connection. Third, it allows one to reorder
the nodes, which determines the order of the dimensions of the
conditional probability table.
A completed Bayesian network has a conditional probability table (CPT)
associated with each node. The CPT provides the conditional
probability distributions of the node given the states of its parents
in the graph. RNetica provides two functions for accessing and
setting this CPT. The function NodeProbs()
returns (or
sets) the conditional probability table as a multi-dimensional
array. However, using the array extractor “[...]”
(Extract.NeticaNode
) allows the conditional probability
table to be manipulated as a data frame, where the first several
columns provide the states of the parent variables, and the remaining
columns the probabilities of the the node being in each of those
states given the parent configurations. This latter approach has a
number of features for working with large tables and tables with
complex structure. The double square bracket extractor
“[[...]]” (Extract.NeticaNode
) words with
logical IsNodeDeterministic
nodes where parent
configuration maps to exactly one state of the child. So rather than
returning a probability distribution, they return a value table.
Finally, when the network is complete, the function
WriteNetworks()
can be used to save it to a file, which
can either be later read into RNetica, or can be used with the Netica
GUI or other applications that use the Netica API.
The basic purpose for building a Bayesian network is to rapidly calculate conditional probabilities. In Netica language, one enters findings (conditions) on the known or hypothesised variables and then calculates beliefs (conditional probabilities) on certain variables of interest.
Netica, like most Bayesian network software, uses two different
graphical representations, one for model construction and one for
inference. The acyclic directed graph is use for model construction
(previous section). The function CompileNetwork()
builds the second graphical representation: the junction tree. The
function JunctionTreeReport()
provides information about
the compiled representation.
While compiling can take a long time (depending on the size and
connectivity of the network), repeated compilations appear to be
harmless. There is an UncompileNetwork()
function, but
performing any editing operation (adding or removing nodes or edges)
will automatically return the network to an uncompiled state. Netica
tries to preserve finding information. In particular the function
AbsorbNodes()
provides a mechanism for removing nodes
from a network without changing the joint probability (including
influence of findings) of the remaining nodes. (The network must be
recompiled after a call to AbsorbNodes()
though.)
The principle way to enter observed evidence is setting
NodeFinding(node) <- value
. The function
NodeLikelihood()
can be used to enter virtual
evidence, however, some care must be taken as it alters the meanings
of several of the other functions.
The conditional (given the entered findings and likelihoods)
probability distribution can be queried at any time using the function
NodeBeliefs(node)
. If the states of a node have been
given numeric values using NodeLevels(node)
, then
NodeExpectedValue(node)
will calculate the expected
numeric value (and the standard deviation). The function
JointProbability(nodelist)
calculates the joint
distribution over a collection of nodes, and the function
FindingsProbability(net)
calculates the prior
probability of all the findings entered into the network. The
function MostProbableConfig(nodelist)
finds the mode of
the joint probability distribution (given the current findings and
likelihood).
Note that in the default state, when findings are entered, the beliefs
about all other nodes in the network are then updated. This can be
time consuming in large networks. The function
SetNetworkAutoUpdate()
can be used to change this to a
lazy updating mode, when the evidence from the findings are only
propagated when required for a call to NodeBeliefs()
or a
similar function. The function
WithoutAutoUpdate(net,expr)
is useful for
setting findings in a large number of nodes in net without the
overhead of belief updating.
The function NodeSets()
allows the modeller to attach
labels to the nodes in the network. For the most part, Netica ignores
these labels, except that it will colour nodes from various sets
different colours (NetworkNodeSetColor()
). Aside from a
few internal labels used by Netica, these node sets are reserved for
user programming.
RNetica provides some functions that make node sets incredibly
convenient ways to describe the intended usage of the nodes. In
particular, the function NetworkNodesInSet()
returns a
list of all nodes which are tagged as being in a particular node set.
For example, suppose that the modeller has marked a number of nodes as
being in the node set "ReportingVar"
. Then the following code
would generate a report about the network:
net.ReportingVars <- NetworkNodesInSet(net, "ReportingVar") lapply(net.ReportingVars, NodeBeliefs)
After a call to a C function in the Netica API, an additional function
must be called to check for errors. It can return messages at
different levels of severity. The reportErrors
method of the
NeticaSession
object calls this function and
bundles it into an object of class link{NeticaConditon}
. The
class of the output of reportErros
is determined by the
messages with the highest severity.
Netica Error Priority | R condition class |
“XXX_ERR” | "NeticaCondition" "fatal" "error"
"condition"
|
“ERROR_ERR” | "NeticaCondition" "error" "
condition"
|
“WARNING_ERR” | "NeticaCondition" "warning"
"condition"
|
“NOTICE_ERR” | "NeticaCondition" "message"
"condition"
|
“REPORT_ERR” | "NeticaCondition" "message"
"condition"
|
no messages | "NULL" |
Note that errors origininating from Netica have the type
“NeticaCondition” so that error class can be separately tracked
by error handlers. The types “error”, “warning”,
“message”, and “condition” are all subclasses of the
base R class condition
, and all NeticaCondition
objects have a message and a call field. The default argument for
call, sets the call field to be the function or method from which
reportErrors
or signalErrors
was called. In addition,
NeticaCondition
objects have fields of type Fatal
,
Error
, Warning
, Notice
and Report
which
are all character vectors giving the text of the messages returned
from Netica.
The NeticaSession
object also has a
signalErrors
method. This calls reportErrors
, and then
if the result is a NeticaCondition
signals the error using the
appropriate method: stop
for (fatal) errors,
warning
for warnings, and
signalCondition
for other conditions. The
NeticaBN
and NeticaNode
objects (as well as some other persistant objects) also have
reportErrors
and signalErrors
methods, which delegate
this function to the session. One of these methods is called after
any RNetica function which calls an internal Netica C API call, so
that these errors will be properly captured.
The current status of RNetica is that of a beta release. The code base is stable enough to do useful work, but more testing is still required. Users are advised to work in such a way that they can easily recover from problems.
In particular, because RNetica calls C code, there is a possibility
that it will crash R. There is also a possibility that pointers
embedded in NeticaBN
and
NeticaNode
objects will become corrupted. If
such problems occur, it is best to restart R and reload the networks.
Please send information about both serious and not-so-serious problems to the maintainer.
Netica and Norsys are registered trademarks of Norsys, LLC, used by permission.
Although Norsys is generally supportive of the RNetica project, it does not officially support RNetica, and all questions should be sent to the package maintainers.
We are grateful to support from the following projects for supporting the work in the development and maintenance of this package.
Bill \& Melinda Gates Foundation grant "Games as Learning/Assessment: Stealth Assessment" (\#0PP1035331, Val Shute, PI)
National Science Foundation grant "DIP: Game-based Assessment and Support of STEM-related Competencies" (\#1628937, Val Shute, PI).
National Scient Foundation grant "Mathematical Learning via Architectual Design and Modeling Using E-Rebuild." (\#1720533, Fengfeng Ke, PI).
I would also like to thank Brent Borlange @ Norsys for answering questions related to the API as well as making improvements to fix issues raised during the construction of RNetica. Although Norsys does not officially support RNetica, their unoffical support has been quite valuable.
Russell Almond
Maintainer: Russell Almond <[email protected]>
The general Netica manual can be found at: http://www.norsys.com/WebHelp/NETICA.htm
The Netica API documentation can be found at http://norsys.com/onLineAPIManual/index.html.
Almond, R. G. & Mislevy, R. J. (1999) Graphical models and computerized adaptive testing. Applied Psychological Measurement, 23, 223–238.
Almond, R. G., Mislevy, R. J., Steinberg, L. S., Yan, D. & Williamson, D. M. (2015) Bayesian Networks in Educational Assessment. Springer.
########################################################### ## Network Construction: sess <- NeticaSession() startSession(sess) abc <- CreateNetwork("ABC", session=sess) A <- NewDiscreteNode(abc,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc,"C",c("C1","C2")) AddLink(A,B) NodeParents(C) <- list(A,B) NodeProbs(A)<-c(.1,.2,.3,.4) NodeProbs(B) <- normalize(matrix(1:12,4,3)) NodeProbs(C) <- normalize(array(1:24,c(4,3,2))) abcFile <- tempfile("peanut",fileext=".dne") WriteNetworks(abc,abcFile) DeleteNetwork(abc) ################################################################### ## Inference using the EM-SM algorithm (Almond & Mislevy, 1999). ## System/Student model EMSMSystem <- ReadNetworks(system.file("sampleNets","System.dne", package="RNetica"), session=sess) ## Evidence model for Task 1a EMTask1a <- ReadNetworks(system.file("sampleNets","EMTask1a.dne", package="RNetica"), session=sess) ## Evidence model for Task 2a EMTask2a <- ReadNetworks(system.file("sampleNets","EMTask2a.dne", package="RNetica"), session=sess) ## Task 1a has a footprint of Skill1 and Skill2 (those are the ## referenced student model nodes. So we want joint the footprint into ## a single clique. MakeCliqueNode(NetworkFindNode(EMSMSystem, NetworkFootprint(EMTask1a))) ## The footprint for Task2 a is already a clique, so no need to do ## anything. ## Make a copy for student 1 student1 <- CopyNetworks(EMSMSystem,"student1") ## Monitor nodes for proficiency student1.prof <- NetworkNodesInSet(student1,"Proficiency") student1.t1a <- AdjoinNetwork(student1,EMTask1a) ## We are done with the original EMTask1a now DeleteNetwork(EMTask1a) ## Now add findings CompileNetwork(student1) NodeFinding(student1.t1a$Obs1a1) <- "Right" NodeFinding(student1.t1a$Obs1a2) <- "Right" student1.probt1a <- JointProbability(student1.prof) ## Done with the observables, absorb them AbsorbNodes(student1.t1a) CompileNetwork(student1) student1.probt1ax <- JointProbability(student1.prof) ## Now Task 2 student1.t2a <- AdjoinNetwork(student1,EMTask2a,"t2a") DeleteNetwork(EMTask2a) ## Add findings CompileNetwork(student1) NodeFinding(student1.t2a$Obs2a) <- "Half" AbsorbNodes(student1.t2a) CompileNetwork(student1) student1.probt1a2ax <- JointProbability(student1.prof) DeleteNetwork(list(student1, EMSMSystem)) stopSession(sess)
########################################################### ## Network Construction: sess <- NeticaSession() startSession(sess) abc <- CreateNetwork("ABC", session=sess) A <- NewDiscreteNode(abc,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc,"C",c("C1","C2")) AddLink(A,B) NodeParents(C) <- list(A,B) NodeProbs(A)<-c(.1,.2,.3,.4) NodeProbs(B) <- normalize(matrix(1:12,4,3)) NodeProbs(C) <- normalize(array(1:24,c(4,3,2))) abcFile <- tempfile("peanut",fileext=".dne") WriteNetworks(abc,abcFile) DeleteNetwork(abc) ################################################################### ## Inference using the EM-SM algorithm (Almond & Mislevy, 1999). ## System/Student model EMSMSystem <- ReadNetworks(system.file("sampleNets","System.dne", package="RNetica"), session=sess) ## Evidence model for Task 1a EMTask1a <- ReadNetworks(system.file("sampleNets","EMTask1a.dne", package="RNetica"), session=sess) ## Evidence model for Task 2a EMTask2a <- ReadNetworks(system.file("sampleNets","EMTask2a.dne", package="RNetica"), session=sess) ## Task 1a has a footprint of Skill1 and Skill2 (those are the ## referenced student model nodes. So we want joint the footprint into ## a single clique. MakeCliqueNode(NetworkFindNode(EMSMSystem, NetworkFootprint(EMTask1a))) ## The footprint for Task2 a is already a clique, so no need to do ## anything. ## Make a copy for student 1 student1 <- CopyNetworks(EMSMSystem,"student1") ## Monitor nodes for proficiency student1.prof <- NetworkNodesInSet(student1,"Proficiency") student1.t1a <- AdjoinNetwork(student1,EMTask1a) ## We are done with the original EMTask1a now DeleteNetwork(EMTask1a) ## Now add findings CompileNetwork(student1) NodeFinding(student1.t1a$Obs1a1) <- "Right" NodeFinding(student1.t1a$Obs1a2) <- "Right" student1.probt1a <- JointProbability(student1.prof) ## Done with the observables, absorb them AbsorbNodes(student1.t1a) CompileNetwork(student1) student1.probt1ax <- JointProbability(student1.prof) ## Now Task 2 student1.t2a <- AdjoinNetwork(student1,EMTask2a,"t2a") DeleteNetwork(EMTask2a) ## Add findings CompileNetwork(student1) NodeFinding(student1.t2a$Obs2a) <- "Half" AbsorbNodes(student1.t2a) CompileNetwork(student1) student1.probt1a2ax <- JointProbability(student1.prof) DeleteNetwork(list(student1, EMSMSystem)) stopSession(sess)
This function deletes NeticaNode
connecting
the parents of the deleted node to its children. If multiple nodes
are passed as the argument, then all of the nodes are absorbed. The
joint probability distribution over the remaining nodes should be the
same as the marginal probability distribution over the remaining nodes
before the nodes were deleted.
AbsorbNodes(nodes)
AbsorbNodes(nodes)
nodes |
A |
This function provides a way of removing a node without affecting the
connectivity, or the joint probability of the remaining nodes. In
particular, all of the relationship tested by
is.NodeRelated()
among the remaining nodes should remain
true (or false) when we are done.
Returns NULL
.
There is a bug in version 5.04 (and 5.10) of the Netica API where
AbsorbNodes can crash if some nodes have visual information and some
do not. For the moment, it is recommended that you call
ReadNetworks
with loadVisual=FALSE
to work around
this problem.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: AbsorbNodes_bn()
NeticaNode
, AddLink()
,
NodeChildren()
, NodeParents()
,
ReverseLink()
, is.NodeRelated()
sess <- NeticaSession() startSession(sess) anet <- CreateNetwork("Absorbent",sess) xnodes <- NewDiscreteNode(anet,paste("X",1:5,sep="_")) AddLink(xnodes[[1]],xnodes[[2]]) AddLink(xnodes[[2]],xnodes[[3]]) AddLink(xnodes[[3]],xnodes[[4]]) AddLink(xnodes[[3]],xnodes[[5]]) stopifnot( all(match(xnodes[4:5],NodeChildren(xnodes[[3]]),nomatch=0)>0), is.NodeRelated(xnodes[[2]],xnodes[[3]],"parent"), is.NodeRelated(xnodes[[2]],xnodes[[1]],"child") ) ## These are leaf nodes, shouldn't change topology, except locally. if (NeticaVersion(sess)$number >600) { AbsorbNodes(xnodes[4:5]) stopifnot( ## Nodes 4 and 5 are now deleted all(!is.active(xnodes[4:5])), all(anet$listNodes() == c("X_1","X_2","X_3")), length(NodeChildren(xnodes[[3]]))==0, is.NodeRelated(xnodes[[2]],xnodes[[3]],"parent"), is.NodeRelated(xnodes[[2]],xnodes[[1]],"child") ) ## This should connect X1->X3 AbsorbNodes(xnodes[[2]]) stopifnot( ## Node 2 is now deleted !is.active(xnodes[[2]]), length(NodeChildren(xnodes[[3]]))==0, is.NodeRelated(xnodes[[1]],xnodes[[3]],"parent"), is.NodeRelated(xnodes[[3]],xnodes[[1]],"child") ) } DeleteNetwork(anet) stopSession(sess)
sess <- NeticaSession() startSession(sess) anet <- CreateNetwork("Absorbent",sess) xnodes <- NewDiscreteNode(anet,paste("X",1:5,sep="_")) AddLink(xnodes[[1]],xnodes[[2]]) AddLink(xnodes[[2]],xnodes[[3]]) AddLink(xnodes[[3]],xnodes[[4]]) AddLink(xnodes[[3]],xnodes[[5]]) stopifnot( all(match(xnodes[4:5],NodeChildren(xnodes[[3]]),nomatch=0)>0), is.NodeRelated(xnodes[[2]],xnodes[[3]],"parent"), is.NodeRelated(xnodes[[2]],xnodes[[1]],"child") ) ## These are leaf nodes, shouldn't change topology, except locally. if (NeticaVersion(sess)$number >600) { AbsorbNodes(xnodes[4:5]) stopifnot( ## Nodes 4 and 5 are now deleted all(!is.active(xnodes[4:5])), all(anet$listNodes() == c("X_1","X_2","X_3")), length(NodeChildren(xnodes[[3]]))==0, is.NodeRelated(xnodes[[2]],xnodes[[3]],"parent"), is.NodeRelated(xnodes[[2]],xnodes[[1]],"child") ) ## This should connect X1->X3 AbsorbNodes(xnodes[[2]]) stopifnot( ## Node 2 is now deleted !is.active(xnodes[[2]]), length(NodeChildren(xnodes[[3]]))==0, is.NodeRelated(xnodes[[1]],xnodes[[3]],"parent"), is.NodeRelated(xnodes[[3]],xnodes[[1]],"child") ) } DeleteNetwork(anet) stopSession(sess)
Add link adds an edge from Parent
to Child
. Delete Link
removes that edge. This states that the distribution of child
will be specified conditional on the value of parent
.
Consequently, adding or removing edges with affect the
conditional probability tables associated with the Child
node
(see NodeProbs()
.)
AddLink(parent, child) DeleteLink(parent, child)
AddLink(parent, child) DeleteLink(parent, child)
parent |
A |
child |
A |
After adding a link parent --> child
, it may be the case that
parent
is in NodeParents(child)
and child
is a member of NodeChildren(parent)
. If child
already has other parents, then the new parent will be added to the
end of the list. The order of the parents can be set by setting
NodeParents(child)
.
In general, the Bayesian network must always be an acyclic directed
graph. Therefore, if parent
is a descendant of child
(that is if is.NodeRelated(child)
, "descendant",
child
is TRUE
), then Netica will generate an error.
The function DeleteLink()
removes the relationship, and the
parent
and child
nodes should no longer be in each
other parent and child lists. The parent list of the child node is
shortened (a stub node for later reconnection is not created as when
NodeParents(child)[i] <- list(NULL)
).
The function AddLinK
invisibly returns the index of the new
parent in the parent list.
The function DeleteLink
invisibly returns the child node.
The Netica API specifies the first argument to DeleteLink_bn()
as an index into the parent list. RNetica maps from the node to the
index.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: AddLink_bn(), DeleteLink_bn()
NeticaNode
, NodeParents()
,
NodeChildren()
, is.NodeRelated()
sess <- NeticaSession() startSession(sess) abnet <- CreateNetwork("AABB", session=sess) A <- NewDiscreteNode(abnet, "A") B <- NewDiscreteNode(abnet, "B") AddLink(A,B) stopifnot( is.element(list(A),NodeParents(B)), is.element(list(B),NodeChildren(A)) ) DeleteLink(A,B) stopifnot( !is.element(list(A),NodeParents(B)), !is.element(list(B),NodeChildren(A)) ) DeleteNetwork(abnet) stopSession(sess)
sess <- NeticaSession() startSession(sess) abnet <- CreateNetwork("AABB", session=sess) A <- NewDiscreteNode(abnet, "A") B <- NewDiscreteNode(abnet, "B") AddLink(A,B) stopifnot( is.element(list(A),NodeParents(B)), is.element(list(B),NodeChildren(A)) ) DeleteLink(A,B) stopifnot( !is.element(list(A),NodeParents(B)), !is.element(list(B),NodeChildren(A)) ) DeleteNetwork(abnet) stopSession(sess)
This function assumes that the two arguments are networks that were
designed to be connected to one another. It copies the nodes from
em
into sm
and then tries to resolve any stub links in
the copied nodes by connecting them to nodes in sm
.
AdjoinNetwork(sm, em, setname = character())
AdjoinNetwork(sm, em, setname = character())
sm |
An active |
em |
An active |
setname |
An optional character vector containing names of node sets (see
|
This follows the System Model–Evidence Model (or Hub-and-spoke)
protocol laid out in Almond et al (1999) and Almond and Mislevy
(1999). The idea is that the network sm
is a complete network
that encodes beliefs about the current status of a system. In
particular, it often encodes the state of knowledge about a student
and is then called a student model.
The second network em
is an incomplete network: a fragment of a
network, some of whose nodes could be stub nodes referring to nodes in
the sm
(see NodeInputNames()
and
NodeKind()
). The idea is that the evidence model
provides a set of observable values associated with some diagnostic
procedure, in particular, a task on an assessment.
The function AdjoinNetwork(sm,em)
copies all of the nodes from
em
to sm
, modifying sm
in the process (copy it
first using CopyNetworks(sm)
if this is not the
intention). It then the parents of each node, emnode
, in
em
looking for stub nodes (cases where
NodeParents(emnode)[j]
has been set to NULL
for some
parent. AdjoinNetworks(sm,em)
then tries to find a
matching parent by searching for a system model node, smnode
named NodeInputNames(emnode)[j]
. If it finds one, it
sets NodeParents(emnode)[j] <- smnode
; if not, it issues a
warning.
The function AdjoinNetwork(sm,em)
also copies node set
information from the nodes in em
to their copies in sm
.
The value of setname
is concatenated with the current node
sets of the nodes in em
. This provides a handy way of
identifying the evidence model from which the nodes came.
After findings are entered on the nodes in the evidence model, the can
be eliminated using AbsorbNodes()
.
A list containing the newly copied nodes (the instances of the
em
nodes now in sm
).
Russell Almond
Almond, R. G. & Mislevy, R. J. (1999) Graphical models and computerized adaptive testing. Applied Psychological Measurement, 23, 223–238.
Almond, R., Herskovits, E., Mislevy, R. J., & Steinberg, L. S. (1999). Transfer of information between system and evidence models. In Artificial Intelligence and Statistics 99, Proceedings (pp. 181–186). Morgan-Kaufman
NeticaNode
, AbsorbNodes()
,
JointProbability()
, NodeSets()
,
CopyNodes()
,NetworkFootprint()
sess <- NeticaSession() startSession(sess) ## System/Student model EMSMSystem <- ReadNetworks(system.file("sampleNets","System.dne", package="RNetica"), session=sess) ## Evidence model for Task 1a EMTask1a <- ReadNetworks(system.file("sampleNets","EMTask1a.dne", package="RNetica"), session=sess) ## Evidence model for Task 2a EMTask2a <- ReadNetworks(system.file("sampleNets","EMTask2a.dne", package="RNetica"), session=sess) ## Evidence model for Task 2b EMTask2b <- ReadNetworks(system.file("sampleNets","EMTask2b.dne", package="RNetica"), session=sess) ## Task 1a has a footprint of Skill1 and Skill2 (those are the ## referenced student model nodes. So we want joint the footprint into ## a single clique. MakeCliqueNode(NetworkFindNode(EMSMSystem, NetworkFootprint(EMTask1a))) ## The footprint for Task2 a is already a clique, so no need to do ## anything. ## Make a copy for student 1 student1 <- CopyNetworks(EMSMSystem,"student1") ## Monitor nodes for proficiency student1.prof <- NetworkNodesInSet(student1,"Proficiency") student1.t1a <- AdjoinNetwork(student1,EMTask1a) stopifnot(setequal(student1$listNodes(), c("CliqueNode1", "Obs1a1", "Obs1a2", "Skill1", "Skill2", "Skill3"))) ## We are done with the original EMTask1a now DeleteNetwork(EMTask1a) ## Now add findings CompileNetwork(student1) NodeFinding(student1.t1a$Obs1a1) <- "Right" NodeFinding(student1.t1a$Obs1a2) <- "Right" student1.probt1a <- JointProbability(student1.prof) ## Done with the observables, absorb them if (NeticaVersion(sess)$number >600) { AbsorbNodes(student1.t1a) stopifnot(setequal(student1$listNodes(), c("CliqueNode1", "Skill1", "Skill2", "Skill3"))) } CompileNetwork(student1) student1.probt1ax <- JointProbability(student1.prof) ## This should be the same stopifnot( sum(abs(student1.probt1a-student1.probt1ax)) <.0001 ) ## Now Task 2 student1.t2a <- AdjoinNetwork(student1,EMTask2a,as.IDname("t2a")) if (NeticaVersion(sess)$number >600) { stopifnot( setequal(names(student1.t2a),names(NetworkNodesInSet(student1,"t2a"))) ) stopifnot(setequal(student1$listNodes(), c("CliqueNode1", "Obs2a", "Skill1", "Skill2", "Skill3"))) } DeleteNetwork(EMTask2a) ## Add findings CompileNetwork(student1) NodeFinding(student1.t2a$Obs2a) <- "Half" student1.probt1a2a <- JointProbability(student1.prof) if (NeticaVersion(sess)$number >600) { AbsorbNodes(student1.t2a) stopifnot(setequal(student1$listNodes(), c("CliqueNode1", "Skill1", "Skill2", "Skill3"))) } CompileNetwork(student1) student1.probt1a2ax <- JointProbability(student1.prof) ## This should be the same stopifnot( sum(abs(student1.probt1a2a-student1.probt1a2ax)) <.0001 ) ## Adjoining networks twice should result in copies with incremented ## numbers. AdjoinNetwork(student1,EMTask2b) AdjoinNetwork(student1,EMTask2b) if (NeticaVersion(sess)$number >600) { stopifnot(setequal(student1$listNodes(), c("CliqueNode1", "Obs2b", "Obs2b1", "Skill1", "Skill2", "Skill3"))) } DeleteNetwork(student1) DeleteNetwork(EMTask2b) DeleteNetwork(EMSMSystem) stopSession(sess)
sess <- NeticaSession() startSession(sess) ## System/Student model EMSMSystem <- ReadNetworks(system.file("sampleNets","System.dne", package="RNetica"), session=sess) ## Evidence model for Task 1a EMTask1a <- ReadNetworks(system.file("sampleNets","EMTask1a.dne", package="RNetica"), session=sess) ## Evidence model for Task 2a EMTask2a <- ReadNetworks(system.file("sampleNets","EMTask2a.dne", package="RNetica"), session=sess) ## Evidence model for Task 2b EMTask2b <- ReadNetworks(system.file("sampleNets","EMTask2b.dne", package="RNetica"), session=sess) ## Task 1a has a footprint of Skill1 and Skill2 (those are the ## referenced student model nodes. So we want joint the footprint into ## a single clique. MakeCliqueNode(NetworkFindNode(EMSMSystem, NetworkFootprint(EMTask1a))) ## The footprint for Task2 a is already a clique, so no need to do ## anything. ## Make a copy for student 1 student1 <- CopyNetworks(EMSMSystem,"student1") ## Monitor nodes for proficiency student1.prof <- NetworkNodesInSet(student1,"Proficiency") student1.t1a <- AdjoinNetwork(student1,EMTask1a) stopifnot(setequal(student1$listNodes(), c("CliqueNode1", "Obs1a1", "Obs1a2", "Skill1", "Skill2", "Skill3"))) ## We are done with the original EMTask1a now DeleteNetwork(EMTask1a) ## Now add findings CompileNetwork(student1) NodeFinding(student1.t1a$Obs1a1) <- "Right" NodeFinding(student1.t1a$Obs1a2) <- "Right" student1.probt1a <- JointProbability(student1.prof) ## Done with the observables, absorb them if (NeticaVersion(sess)$number >600) { AbsorbNodes(student1.t1a) stopifnot(setequal(student1$listNodes(), c("CliqueNode1", "Skill1", "Skill2", "Skill3"))) } CompileNetwork(student1) student1.probt1ax <- JointProbability(student1.prof) ## This should be the same stopifnot( sum(abs(student1.probt1a-student1.probt1ax)) <.0001 ) ## Now Task 2 student1.t2a <- AdjoinNetwork(student1,EMTask2a,as.IDname("t2a")) if (NeticaVersion(sess)$number >600) { stopifnot( setequal(names(student1.t2a),names(NetworkNodesInSet(student1,"t2a"))) ) stopifnot(setequal(student1$listNodes(), c("CliqueNode1", "Obs2a", "Skill1", "Skill2", "Skill3"))) } DeleteNetwork(EMTask2a) ## Add findings CompileNetwork(student1) NodeFinding(student1.t2a$Obs2a) <- "Half" student1.probt1a2a <- JointProbability(student1.prof) if (NeticaVersion(sess)$number >600) { AbsorbNodes(student1.t2a) stopifnot(setequal(student1$listNodes(), c("CliqueNode1", "Skill1", "Skill2", "Skill3"))) } CompileNetwork(student1) student1.probt1a2ax <- JointProbability(student1.prof) ## This should be the same stopifnot( sum(abs(student1.probt1a2a-student1.probt1a2ax)) <.0001 ) ## Adjoining networks twice should result in copies with incremented ## numbers. AdjoinNetwork(student1,EMTask2b) AdjoinNetwork(student1,EMTask2b) if (NeticaVersion(sess)$number >600) { stopifnot(setequal(student1$listNodes(), c("CliqueNode1", "Obs2b", "Obs2b1", "Skill1", "Skill2", "Skill3"))) } DeleteNetwork(student1) DeleteNetwork(EMTask2b) DeleteNetwork(EMSMSystem) stopSession(sess)
The expression CalcNodeState(node)
will return the state of
node
if it is known deterministically, and NA
if the exact
value is not known. The expression CalcNodeValue(node)
will
return the numeric value of the node (e.g., the value set with
NodeLevels(node)
.
CalcNodeState(node) CalcNodeValue(node)
CalcNodeState(node) CalcNodeValue(node)
node |
An active |
According to the Netica manual, the way that the value of node
could be known absolutely is if it was set directly a call to
NodeFinding(node)
or NodeValue(node)
, or
if the value can be calculated exactly through logical conditional
probability tables (i.e., ones with just 0's and 1's) or formula (see
NodeEquation()
.
The expression CalcNodeState(node)
is appropriate when
node
is discrete, or has been discretized through a call to
NodeLevels(node)
. Otherwise it will generate an error.
The expression CalcNodeValue(node)
is appropriate when
node
is continuous, or the states have been assigned numeric
values through a call to NodeLevels(node)
. Otherwise it
will generate an error.
The expression CalcNodeState(node)
will return a
character scalar giving the name of the current state of node
if it can be determined, otherwise it will return NA
.
The expression CalcNodeValue(node)
will return a
numeric scalar giving the name of the current value of node
if it can be determined, otherwise it will return NA
.
This function is not behaving at all like what I expected. In
particular, it is returning NA
in many cases where I expect it
to produce a value. I've queried Norsys about this, but use with caution
until I get a clarification.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: CalcNodeState_bn(), CalcNodeValue_bn()
NodeFinding()
, NodeLevels()
,
NodeValue()
, IsNodeDeterministic()
,
NodeEquation()
,is.continuous()
,
NodeExpectedValue()
sess <- NeticaSession() startSession(sess) lights <- CreateNetwork("lights", session=sess) switchs <- NewDiscreteNode(lights,paste("Switch",1:2,sep=""),c("Up","Down")) bulb <- NewDiscreteNode(lights,"Bulb",c("On","Off")) ## Set up a two-way switch (Xor) network AddLink(switchs[[1]],bulb) AddLink(switchs[[2]],bulb) ## This sets up a logical table, so that the light is on iff ## both switches are in the same orientation. bulb[] <-"Off" bulb[Switch1="Up",Switch2="Up"]<-"On" bulb[Switch1="Down",Switch2="Down"]<-"On" switchs[[1]][] <- .5 switchs[[2]][] <- .5 CompileNetwork(lights) ## Bulb is a deterministic node. stopifnot(IsNodeDeterministic(bulb)) ## value of node is unknown, returns NA stopifnot(is.na(CalcNodeState(bulb))) NodeFinding(switchs[[1]]) <- "Up" NodeFinding(switchs[[2]]) <- "Up" stopifnot(CalcNodeState(switchs[[1]])=="Up") stopifnot(CalcNodeState(bulb)=="On") NodeLevels(bulb) <-c(1,0) NodeLevels(switchs[[1]]) <-c(1,0) NodeLevels(switchs[[2]]) <-c(1,0) ## I expect both of these to return 1, but they return NA CalcNodeValue(bulb) CalcNodeValue(switchs[[1]]) DeleteNetwork(lights) stopSession(sess)
sess <- NeticaSession() startSession(sess) lights <- CreateNetwork("lights", session=sess) switchs <- NewDiscreteNode(lights,paste("Switch",1:2,sep=""),c("Up","Down")) bulb <- NewDiscreteNode(lights,"Bulb",c("On","Off")) ## Set up a two-way switch (Xor) network AddLink(switchs[[1]],bulb) AddLink(switchs[[2]],bulb) ## This sets up a logical table, so that the light is on iff ## both switches are in the same orientation. bulb[] <-"Off" bulb[Switch1="Up",Switch2="Up"]<-"On" bulb[Switch1="Down",Switch2="Down"]<-"On" switchs[[1]][] <- .5 switchs[[2]][] <- .5 CompileNetwork(lights) ## Bulb is a deterministic node. stopifnot(IsNodeDeterministic(bulb)) ## value of node is unknown, returns NA stopifnot(is.na(CalcNodeState(bulb))) NodeFinding(switchs[[1]]) <- "Up" NodeFinding(switchs[[2]]) <- "Up" stopifnot(CalcNodeState(switchs[[1]])=="Up") stopifnot(CalcNodeState(bulb)=="On") NodeLevels(bulb) <-c(1,0) NodeLevels(switchs[[1]]) <-c(1,0) NodeLevels(switchs[[2]]) <-c(1,0) ## I expect both of these to return 1, but they return NA CalcNodeValue(bulb) CalcNodeValue(switchs[[1]]) DeleteNetwork(lights) stopSession(sess)
The function CaseFileDelimiter
sets the field delimiter used
when writing case files. The function CaseFileMissingCode
sets
the character code used for missing values in case files. If called
with a null argument, then the current value is returned.
CaseFileDelimiter(newdelimiter = NULL, session=getDefaultSession()) CaseFileMissingCode(newcode = NULL, session=getDefaultSession())
CaseFileDelimiter(newdelimiter = NULL, session=getDefaultSession()) CaseFileMissingCode(newcode = NULL, session=getDefaultSession())
newdelimiter |
A character scalar containing the new delimiter. It must be either a comma, a space, or a tab. |
session |
An object of type |
newcode |
The character to be used as a delimiter. It must be either an asterisk ("*"), a question mark ("?"), a space, (" ") or the empty string (""). |
Case files are essentially a comma separated value files, although tab and space are allowed as alternative delimiters. The space and empty string are only allowed as missing value codes when the delimiter is a comma.
The value of the delimiter is global, and applies to all case files written from this point on.
When the argument is null (the default) the current value is returned without changing it.
The value of the delimiter or missing code before the function call as a string.
The default R missing code "NA" does not work with Netica.
Russell G. Almond
http://norsys.com/onLineAPIManual/index.html: SetCaseFileDelimChar_ns(), SetMissingDataChar_ns()
WriteFindings
, WriteFindings
,
read.CaseFile
, CaseStream
sess <- NeticaSession() startSession(sess) defaultDelim <- CaseFileDelimiter(session=sess) # Get default d1 <- CaseFileDelimiter("\t", session=sess) d2 <- CaseFileDelimiter(" ", session=sess) d3 <- CaseFileDelimiter(",", session=sess) defaultMiss <- CaseFileMissingCode(session=sess) # Get default m1 <- CaseFileMissingCode("*", session=sess) m2 <- CaseFileMissingCode("?", session=sess) m3 <- CaseFileMissingCode(" ", session=sess) m4 <- CaseFileMissingCode("", session=sess) ## Not run: ## This should throw an error. CaseFileDelimiter(" ", session=sess) ## End(Not run) m5 <- CaseFileMissingCode("?", session=sess) d4<- CaseFileDelimiter(" ", session=sess) ## Not run: ## This should throw an error CaseFileMissingCode(" ", session=sess) ## End(Not run) ## But this is okay CaseFileMissingCode("*", session=sess) stopifnot(d1==defaultDelim, d2=="\t", d3==" ", d4==",") stopifnot(m1==defaultMiss, m2=="*", m3=="?", m4==" ", m5=="") ## restore defaults CaseFileDelimiter(defaultDelim, session=sess) CaseFileMissingCode(defaultMiss, session=sess) stopSession(sess)
sess <- NeticaSession() startSession(sess) defaultDelim <- CaseFileDelimiter(session=sess) # Get default d1 <- CaseFileDelimiter("\t", session=sess) d2 <- CaseFileDelimiter(" ", session=sess) d3 <- CaseFileDelimiter(",", session=sess) defaultMiss <- CaseFileMissingCode(session=sess) # Get default m1 <- CaseFileMissingCode("*", session=sess) m2 <- CaseFileMissingCode("?", session=sess) m3 <- CaseFileMissingCode(" ", session=sess) m4 <- CaseFileMissingCode("", session=sess) ## Not run: ## This should throw an error. CaseFileDelimiter(" ", session=sess) ## End(Not run) m5 <- CaseFileMissingCode("?", session=sess) d4<- CaseFileDelimiter(" ", session=sess) ## Not run: ## This should throw an error CaseFileMissingCode(" ", session=sess) ## End(Not run) ## But this is okay CaseFileMissingCode("*", session=sess) stopifnot(d1==defaultDelim, d2=="\t", d3==" ", d4==",") stopifnot(m1==defaultMiss, m2=="*", m3=="?", m4==" ", m5=="") ## restore defaults CaseFileDelimiter(defaultDelim, session=sess) CaseFileMissingCode(defaultMiss, session=sess) stopSession(sess)
This is the constructor for FileCaseStream
objects which provide a wrapper around a Netica stream which is used
to read/write cases. In this subclass, the case stream is associated
with a Netica case file (‘.cas’ extension). The function
ReadFindings
reads the findings from the stream and the
function WriteFindings
writes them out.
CaseFileStream(pathname, session=getDefaultSession()) is.CaseFileStream(x) getCaseStreamPath(stream)
CaseFileStream(pathname, session=getDefaultSession()) is.CaseFileStream(x) getCaseStreamPath(stream)
pathname |
A character scalar giving a path to the case file. Netica expects case files to end with the extension ".cas" |
session |
An object of type |
stream |
A |
x |
A object to be printed or whose type is to be determined. |
A FileCaseStream
object is a subclass of the
CaseStream
object, which is an R wrapper around a
Netica stream object, in this case one that reads or writes to a case
file. Case files are tab (or comma, see
CaseFileDelimiter
) separated value files where columns
represent variables and rows represent cases. Although the function
WriteFindings
always appends a new case to the end of a
file (and hence does not need to keep the stream object open between
calls), the function ReadFindings
will read (by default)
sequentially from the cases in the stream, and hence the stream needs
to be kept open between calls.
The function CaseFileStream
will open a stream in Netica and
create a new FileCaseStream
if necessary. The argument
pathname
should be the pathname of the case file in the file
system. This file should be a file previously written by
WriteFindings
or be in the same format. The delimiter
used should be the one given by CaseFileDelimiter
, and
the code used for missing values should be the value of
CaseFileMissingCode
.
The function CloseCaseStream
closes an open case stream
(and is harmless if the stream is already closed). Although RNetica
tries to close open case streams when they are garbage collected,
users should not count on this behavior and should close them
manually. Also be aware that all case streams are automatically
closed when R is closed or RNetica is unloaded. The function
isCaseStreamOpen
tests to see if the stream is open or
closed, and the function OpenCaseStream
reopens a
previously closed case stream.
The functions getCaseStreamPath
returns the path on which the
FileCaseStream
is focused.
The function CaseFileStream
returns a new, open
FileCaseStream
object.
The functions is.CaseFileStream
returns a logical value
indicating whether or not the argument is a CaseFileStream
.
The function getCaseStreamPath
returns a string giving the path
of the file associated with stream
, or NULL
if the
argument is not a CaseFileStream
.
Internally, a weak reference system is used to keep a list of Netica stream objects which need to be closed when RNetica is unloaded. Stream objects should also be forced closed when garbage collected. The weak reference system is somewhat experimental, so well designed code should manually close the streams when the program is through with it.
Stream objects are fragile, and will not survive saving and restoring
an R session. However, the object retains information about itself,
so that calling OpenCaseStream
on the saved object, should
reopen the stream. Note that any position information will be lost.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: NewFileStream_ns(), http://homepage.stat.uiowa.edu/~luke/R/references/weakfinex.html
See FileCaseStream
for properties of file case
stream objects and CaseStream
for general
properties of Netica streams.
CaseFileDelimiter
, CaseFileMissingCode
,
CaseMemoryStream
,
WriteFindings
, ReadFindings
,
sess <- NeticaSession() startSession(sess) abc <- CreateNetwork("ABC", sess) A <- NewDiscreteNode(abc,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc,"C",c("C1","C2")) AddLink(A,B) AddLink(A,C) AddLink(B,C) ## Outputfilename casefile <- tempfile("testcase",fileext=".cas") filestream <- CaseFileStream(casefile, session=sess) stopifnot(is.CaseFileStream(filestream), isCaseStreamOpen(filestream)) ## Case 1 NodeFinding(A) <- "A1" NodeFinding(B) <- "B1" NodeFinding(C) <- "C1" filestream <- WriteFindings(list(A,B,C),filestream,1001,1.0) stopifnot(getCaseStreamLastId(filestream)==1001, abs(getCaseStreamLastFreq(filestream)-1.0) <.0001) ## Close it filestream <- CloseCaseStream(filestream) stopifnot (is.CaseFileStream(filestream), !isCaseStreamOpen(filestream)) ## Reopen it filestream <- OpenCaseStream(filestream) stopifnot (is.CaseFileStream(filestream), isCaseStreamOpen(filestream)) ##Case 1 RetractNetFindings(abc) filestream <- ReadFindings(list(A,B,C),filestream,"FIRST") stopifnot(getCaseStreamLastId(filestream)==1001, abs(getCaseStreamLastFreq(filestream)-1.0) <.0001) ##Clean Up CloseCaseStream(filestream) DeleteNetwork(abc) stopSession(sess)
sess <- NeticaSession() startSession(sess) abc <- CreateNetwork("ABC", sess) A <- NewDiscreteNode(abc,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc,"C",c("C1","C2")) AddLink(A,B) AddLink(A,C) AddLink(B,C) ## Outputfilename casefile <- tempfile("testcase",fileext=".cas") filestream <- CaseFileStream(casefile, session=sess) stopifnot(is.CaseFileStream(filestream), isCaseStreamOpen(filestream)) ## Case 1 NodeFinding(A) <- "A1" NodeFinding(B) <- "B1" NodeFinding(C) <- "C1" filestream <- WriteFindings(list(A,B,C),filestream,1001,1.0) stopifnot(getCaseStreamLastId(filestream)==1001, abs(getCaseStreamLastFreq(filestream)-1.0) <.0001) ## Close it filestream <- CloseCaseStream(filestream) stopifnot (is.CaseFileStream(filestream), !isCaseStreamOpen(filestream)) ## Reopen it filestream <- OpenCaseStream(filestream) stopifnot (is.CaseFileStream(filestream), isCaseStreamOpen(filestream)) ##Case 1 RetractNetFindings(abc) filestream <- ReadFindings(list(A,B,C),filestream,"FIRST") stopifnot(getCaseStreamLastId(filestream)==1001, abs(getCaseStreamLastFreq(filestream)-1.0) <.0001) ##Clean Up CloseCaseStream(filestream) DeleteNetwork(abc) stopSession(sess)
This object is subclass of CaseStream
so it is a
wrapper around a Netica stream which is used to read/write cases. In
this subclass, the case stream is associated with a memory buffer that
corresponds to an R data.frame
object.
The function MemoryStreamContents
accesses the contents
as a data frame.
CaseMemoryStream(data.frame, label=deparse(substitute(data.frame)), session=getDefaultSession()) is.MemoryCaseStream(x) getCaseStreamDataFrameName(stream)
CaseMemoryStream(data.frame, label=deparse(substitute(data.frame)), session=getDefaultSession()) is.MemoryCaseStream(x) getCaseStreamDataFrameName(stream)
data.frame |
A data frame in which columns correspond to Netica nodes, and rows correspond to cases. See details. |
label |
A name for the stream object. |
session |
An object of type |
stream |
A |
x |
A object whose type is to be determined. |
A Netica case file has a format that very much resembles the output of
write.table
. The first row is a header row, which
contains the names of the variables, the second and subsequent rows
contain a set of findings: an assignment of values to the nodes
indicated in the columns. There are no row numbers, and the separator
and missing value codes are the values of
CaseFileDelimiter()
, and
CaseFileMissingCode()
respectively.
In addition to columns representing variables, two special columns are
allowed. The column named “IDnum”, if present should contain
integers which correspond to ID numbers for the cases (this correspond
to the id
argument of WriteFindings
). The column
named “NumCases” should contain number values and this allows
rows to be differentially weighted (this correspond to the freq
argument of WriteFindings
).
A simple way to convert a data frame into a set of cases for use with
various Netica functions that use cases would be to write the data
frame to a file of the proper format, and then create a
CaseFileStream
on the just written file. The
MemoryCaseStream
shortcuts that process by writing the data
frame to a memory buffer and then creating a stream around the memory
buffer. Like the CaseFileStream
, the MemoryCaseStream
is
a subclass of CaseStream
and follows the same
conventions.
The function MemoryCaseStream
opens a new memory stream using
data.frame
as the source. If data.frame
is NULL
a new memory stream for writing is created. The function
CloseCaseStream
closes an open case stream (and is harmless if
the stream is already closed. Although RNetica tries to close open
case streams when they are garbage collected, users should not count
on this behavior and should close them manually. Also be aware that
all case streams are automatically closed when R is closes or RNetica
is unloaded. The function isCaseStreamOpen
tests to see if the
stream is open or closed. The function OpenCaseStream
if
called on a closed MemoryCaseStream
will reopen the stream in Netica
using the current value of MemoryStreamContents
as the
source. (If called on an open stream it will do nothing but issue a
warning).
The function getCaseStreamDataFrameName
provides the value of
label
when the stream was created.
The function OpenMemoryCaseStream
returns a new, open
CaseFileStream
object.
The functions is.MemoryCaseStream
returns a logical value
indicating whether or not the argument is a CaseFileStream
.
The function getCaseStreamDataFrameName
returns the value of
label
used when the stream was created, usually this is the
name of the data.frame
argument.
In version 5.04 of the Netica API, there is a problem with using
Memory Streams that seems to affect the functions
LearnCases
and LearnCPTs
. Until this
problem is fixed, most uses of Memory Streams will require file
streams instead. Write the case file using
write.CaseFile
, and then create a file stream using
CaseFileStream
.
In version 0.5 of RNetica, this class was renamed. It is now called
MemoryCaseStream
and the constructor is called
CaseMemoryStream
(while previously the class and the
filename had the same name). This matches the usage of
FileCaseStream
and its constructor
CaseFileStream
.
MemoryCaseStreams
are most useful for small to medium size data
frames. Larger data frames are probably better handled through case
files.
Internally, a weak reference system is used to keep a list of Netica stream objects which need to be closed when RNetica is unloaded. Stream objects should also be forced closed when garbage collected. The weak reference system is somewhat experimental, so well designed code should manually close the streams when the program is through with it.
Stream objects are fragile, and will not survive saving and restoring
an R session. However, the object retains information about itself,
so that calling OpenCaseStream
on the saved object, should
reopen the stream. Note that any position information will be lost.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: NewMemoryStream_ns(), http://homepage.stat.uiowa.edu/~luke/R/references/weakfinex.html
CaseFileDelimiter
, CaseFileMissingCode
,
WriteFindings
, ReadFindings
,
MemoryStreamContents
,CaseStream
sess <- NeticaSession() startSession(sess) abc <- CreateNetwork("ABC", session=sess) A <- NewDiscreteNode(abc,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc,"C",c("C1","C2")) AddLink(A,B) AddLink(A,C) AddLink(B,C) ## This is the file written in CaseFileStream help. casefile <- system.file("testData","abctestcases.cas", package="RNetica") CaseFileDelimiter("\t", session=sess) CaseFileMissingCode("*", session=sess) cases <- read.CaseFile(casefile, session=sess) memstream <- CaseMemoryStream(cases, session=sess) ##Case 1 memstream <- ReadFindings(list(A,B,C),memstream,"FIRST") stopifnot(NodeFinding(A) == "A1", NodeFinding(B) == "B1", NodeFinding(C) == "C1", getCaseStreamLastId(memstream)==1001, abs(getCaseStreamLastFreq(memstream)-1.0) <.0001) ##Case 2 memstream <- ReadFindings(list(A,B,C),memstream,"NEXT") stopifnot(NodeFinding(A) == "A2", NodeFinding(B) == "B2", NodeFinding(C) == "C2", getCaseStreamLastId(memstream)==1002, abs(getCaseStreamLastFreq(memstream)-2.0) <.0001) ##Case 3 memstream <- ReadFindings(list(A,B,C),memstream,"NEXT") stopifnot(NodeFinding(A) == "A3", NodeFinding(B) == "B3", NodeFinding(C) == "@NO FINDING", getCaseStreamLastId(memstream)==1003, abs(getCaseStreamLastFreq(memstream)-1.0) <.0001) ## EOF memstream <- ReadFindings(list(A,B,C),memstream,"NEXT") stopifnot (is.na(getCaseStreamPos(memstream))) ##Clean Up CloseCaseStream(memstream) DeleteNetwork(abc) stopSession(sess)
sess <- NeticaSession() startSession(sess) abc <- CreateNetwork("ABC", session=sess) A <- NewDiscreteNode(abc,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc,"C",c("C1","C2")) AddLink(A,B) AddLink(A,C) AddLink(B,C) ## This is the file written in CaseFileStream help. casefile <- system.file("testData","abctestcases.cas", package="RNetica") CaseFileDelimiter("\t", session=sess) CaseFileMissingCode("*", session=sess) cases <- read.CaseFile(casefile, session=sess) memstream <- CaseMemoryStream(cases, session=sess) ##Case 1 memstream <- ReadFindings(list(A,B,C),memstream,"FIRST") stopifnot(NodeFinding(A) == "A1", NodeFinding(B) == "B1", NodeFinding(C) == "C1", getCaseStreamLastId(memstream)==1001, abs(getCaseStreamLastFreq(memstream)-1.0) <.0001) ##Case 2 memstream <- ReadFindings(list(A,B,C),memstream,"NEXT") stopifnot(NodeFinding(A) == "A2", NodeFinding(B) == "B2", NodeFinding(C) == "C2", getCaseStreamLastId(memstream)==1002, abs(getCaseStreamLastFreq(memstream)-2.0) <.0001) ##Case 3 memstream <- ReadFindings(list(A,B,C),memstream,"NEXT") stopifnot(NodeFinding(A) == "A3", NodeFinding(B) == "B3", NodeFinding(C) == "@NO FINDING", getCaseStreamLastId(memstream)==1003, abs(getCaseStreamLastFreq(memstream)-1.0) <.0001) ## EOF memstream <- ReadFindings(list(A,B,C),memstream,"NEXT") stopifnot (is.na(getCaseStreamPos(memstream))) ##Clean Up CloseCaseStream(memstream) DeleteNetwork(abc) stopSession(sess)
"CaseStream"
This object is a wrapper around a Netica stream which is used to
read/write cases—sets of findings entered into a Netica network.
There are two subclasses: FileCaseStream
and
MemoryCaseStream
. The function
ReadFindings
reads the findings from the stream and the
function WriteFindings
writes them out.
A CaseStream
object is an R wrapper around a Netica stream
object. There are two subclasses:
FileCaseStream
objects are streams focused on a
case file, and MemoryCaseStream
objects are
streams focused on a hunk of memory corresponding to an R data frame
object.
Although the function WriteFindings
always appends a new
case to the end of a file (and hence does not need to keep the stream
object open between calls), the function ReadFindings
will read (by default) sequentially from the cases in the stream, and
hence the stream needs to be kept open between calls.
The functions CaseFileStream
and
CaseMemoryStream
create new streams and open them.
The function OpenCaseStream
will reopen a previously closed
stream, and will issue a warning if the stream is already open.
The function CloseCaseStream
closes an open case stream (and is
harmless if the stream is already closed). Although RNetica tries to
close open case streams when they are garbage collected, users should
not count on this behavior and should close them manually. Also be
aware that all case streams are automatically closed when R is closes
or RNetica is unloaded. The function isCaseStreamOpen
tests to see if the stream is open or closed. The function
WithOpenCaseStream
executes an arbitrary R expression in
a context where the stream is open, and then closed afterwards.
Netica internally keeps track of the current position of the stream
when it is read or written. The functions getCaseStreamPos
,
getCaseStreamLastId
and
getCaseStreamLastFreq
get
information about the position in the file, the user generated id
number and the frequency/weight assigned to the case at the time the
stream was last read or written. In particular, the function
ReadFindings
returns a CaseStream
object, which
should be queried to find the ID and Frequencies read from the
stream. When ReadFindings
reaches the end of the
stream, the value of getCaseStreamPos(stream)
will
be NA
.
All reference classes extend and inherit methods from
"envRefClass"
. Note that because this is a reference
class unlike traditional S3 and S4 classes it can be destructively
modified. Also fields (slots) are accessed using the ‘$’
operator.
Note these should be regarded as read-only from user code.
Name
:Object of class character
an identifier
for the case stream, derived from the filename for
FileCaseStream
objects, and from the name of
the R object for MemoryCaseStream
Session
:Object of class NeticaSession
:: a back
pointer to the NeticaSession
object in which
the stream was created.
Netica_Case_Stream
:Object of class externalptr
a link to the stream in internal Netica memory.
Case_Stream_Position
:Object of class integer
the number of the last read/writen record. This is NA
if
the end of the file has been reached.
Case_Stream_Lastid
:Object of class integer
the
ID number of the last read/written record.
Case_Stream_Lastfreq
:Object of class numeric
giving the frequence of the last read/written record. This is
used as a weight in learning applications.
show()
:Provides a printed record.
close()
: Closes the stream. Equivalent to
CloseCaseStream(stream)
.
isOpen()
: Checks to see if the stream is currently
open. Equivalent to isCaseStreamOpen(stream)
.
isActive()
: Equivalent to isOpen()
, name is
symmetric with other Netica reference objects.
clearErrors(severity)
: Calls clearErrors
on
the Session
object.
reportErrors(maxreport, clear, call)
: Calls
reportErrors
on the Session
object. Returns an
object of class NeticaCondition
if there was a
message, or NULL
if not.
signalErrors(maxreport, clear, call)
: Calls
signalErrors
on the Session
object. If there was a
problem, the appropriate condition is signaled, see
NeticaCondition
.
initialize(Name, Session, ...)
:Internal initializer. User code should not call.
The functions ReadNetworks
and
WriteNetworks
also use Netica streams
internally. However, as it is almost certainly a mistake to keep the
stream open after the network has been read or written, no
NeticaCaseStream
object is created.
Internally, a weak reference system is used to keep a list of Netica stream objects which need to be closed when RNetica is unloaded. Stream objects should also be forced closed when garbage collected. The weak reference system is somewhat experimental, so well designed code should manually close the streams when the program is through with them.
Stream objects are fragile, and will not survive saving and restoring
an R session. However, the object retains information about itself,
so that calling OpenCaseStream
on the saved object, should
reopen the stream. Note that any position information will be lost.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: NewFileStream_ns(),NewMemoryStream_ns(), DeleteStream_ns() http://homepage.stat.uiowa.edu/~luke/R/references/weakfinex.html
See FileCaseStream
and
MemoryCaseStream
for specific details about the
two subtypes. CaseMemoryStream
and
CaseFileStream
are the two constructors.
OpenCaseStream
,
CaseFileDelimiter
, CaseFileMissingCode
,
WriteFindings
, ReadFindings
,
WithOpenCaseStream
sess <- NeticaSession() startSession(sess) abc <- CreateNetwork("ABC",sess) A <- NewDiscreteNode(abc,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc,"C",c("C1","C2")) AddLink(A,B) AddLink(A,C) AddLink(B,C) ## Outputfilename casefile <- tempfile("testcase",fileext=".cas") filestream <- CaseFileStream(casefile,sess) stopifnot(is.NeticaCaseStream(filestream), isCaseStreamOpen(filestream)) ## Case 1 NodeFinding(A) <- "A1" NodeFinding(B) <- "B1" NodeFinding(C) <- "C1" filestream <- WriteFindings(list(A,B,C),filestream,1001,1.0) stopifnot(getCaseStreamLastId(filestream)==1001, abs(getCaseStreamLastFreq(filestream)-1.0) <.0001) pos1 <- getCaseStreamPos(filestream) RetractNetFindings(abc) ## Case 2 NodeFinding(A) <- "A2" NodeFinding(B) <- "B2" NodeFinding(C) <- "C2" ## Double weight this case filestream <- WriteFindings(list(A,B,C),filestream,1002,2.0) pos2 <- getCaseStreamPos(filestream) stopifnot(pos2>pos1,getCaseStreamLastId(filestream)==1002, abs(getCaseStreamLastFreq(filestream)-2.0) <.0001) RetractNetFindings(abc) ## Case 3 NodeFinding(A) <- "A3" NodeFinding(B) <- "B3" ## C will be missing filestream <- WriteFindings(list(A,B,C),filestream,1003,1.0) stopifnot(getCaseStreamLastId(filestream)==1003, abs(getCaseStreamLastFreq(filestream)-1.0) <.0001) RetractNetFindings(abc) ## Close it filestream <- CloseCaseStream(filestream) stopifnot (is.NeticaCaseStream(filestream), !isCaseStreamOpen(filestream)) ## Reopen it filestream <- OpenCaseStream(filestream) stopifnot (is.NeticaCaseStream(filestream), isCaseStreamOpen(filestream)) ##Case 1 RetractNetFindings(abc) filestream <- ReadFindings(list(A,B,C),filestream,"FIRST") pos1a <- getCaseStreamPos(filestream) stopifnot(pos1a==pos1, getCaseStreamLastId(filestream)==1001, abs(getCaseStreamLastFreq(filestream)-1.0) <.0001) ##Case 2 RetractNetFindings(abc) filestream <- ReadFindings(list(A,B,C),filestream,"NEXT") stopifnot(getCaseStreamPos(filestream)==pos2, getCaseStreamLastId(filestream)==1002, abs(getCaseStreamLastFreq(filestream)-2.0) <.0001) ##Clean Up CloseCaseStream(filestream) CloseCaseStream(filestream) ## This should issue a warning but be ## harmless. DeleteNetwork(abc) stopSession(sess)
sess <- NeticaSession() startSession(sess) abc <- CreateNetwork("ABC",sess) A <- NewDiscreteNode(abc,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc,"C",c("C1","C2")) AddLink(A,B) AddLink(A,C) AddLink(B,C) ## Outputfilename casefile <- tempfile("testcase",fileext=".cas") filestream <- CaseFileStream(casefile,sess) stopifnot(is.NeticaCaseStream(filestream), isCaseStreamOpen(filestream)) ## Case 1 NodeFinding(A) <- "A1" NodeFinding(B) <- "B1" NodeFinding(C) <- "C1" filestream <- WriteFindings(list(A,B,C),filestream,1001,1.0) stopifnot(getCaseStreamLastId(filestream)==1001, abs(getCaseStreamLastFreq(filestream)-1.0) <.0001) pos1 <- getCaseStreamPos(filestream) RetractNetFindings(abc) ## Case 2 NodeFinding(A) <- "A2" NodeFinding(B) <- "B2" NodeFinding(C) <- "C2" ## Double weight this case filestream <- WriteFindings(list(A,B,C),filestream,1002,2.0) pos2 <- getCaseStreamPos(filestream) stopifnot(pos2>pos1,getCaseStreamLastId(filestream)==1002, abs(getCaseStreamLastFreq(filestream)-2.0) <.0001) RetractNetFindings(abc) ## Case 3 NodeFinding(A) <- "A3" NodeFinding(B) <- "B3" ## C will be missing filestream <- WriteFindings(list(A,B,C),filestream,1003,1.0) stopifnot(getCaseStreamLastId(filestream)==1003, abs(getCaseStreamLastFreq(filestream)-1.0) <.0001) RetractNetFindings(abc) ## Close it filestream <- CloseCaseStream(filestream) stopifnot (is.NeticaCaseStream(filestream), !isCaseStreamOpen(filestream)) ## Reopen it filestream <- OpenCaseStream(filestream) stopifnot (is.NeticaCaseStream(filestream), isCaseStreamOpen(filestream)) ##Case 1 RetractNetFindings(abc) filestream <- ReadFindings(list(A,B,C),filestream,"FIRST") pos1a <- getCaseStreamPos(filestream) stopifnot(pos1a==pos1, getCaseStreamLastId(filestream)==1001, abs(getCaseStreamLastFreq(filestream)-1.0) <.0001) ##Case 2 RetractNetFindings(abc) filestream <- ReadFindings(list(A,B,C),filestream,"NEXT") stopifnot(getCaseStreamPos(filestream)==pos2, getCaseStreamLastId(filestream)==1002, abs(getCaseStreamLastFreq(filestream)-2.0) <.0001) ##Clean Up CloseCaseStream(filestream) CloseCaseStream(filestream) ## This should issue a warning but be ## harmless. DeleteNetwork(abc) stopSession(sess)
"CliqueNode"
A dummy node used to force it parents into the same clique in the
junction tree. In particular, the node has a single state but its
parents are listed in its clique
field.
Class "NeticaNode"
, directly.
All reference classes extend and inherit methods from
"envRefClass"
. Note that because this is a
reference class unlike traditional S3 and S4 classes it can be
destructively modified. Also fields (slots) are accessed using the
‘$’ operator.
signature(x = "CliqueNode")
: Provides a
pretited representation.
Note these should be regarded as read-only from user code.
Name
:Object of class character
giving the
Netica name of the node. Must follow the IDname
rules.
Netica_Node
:Object of class externalptr
giving
the address of the node in Netica's memory space.
Net
:Object of class NeticaBN
, a
back reference to the network in which this node resides.
discrete
:Always TRUE
for clique nodes.
clique
:A list of NeticaNode
objects which are the parents of the clique node.
show()
:Prints a description of the node.
initialize(..., clique)
: Internal initializer, should
not be called directly by user code. Use
MakeCliqueNode
instead.
The following methods are inherited (from the NeticaNode
):
deactivate ("NeticaNode"), isActive ("NeticaNode"), show ("NeticaNode"),
clearErrors ("NeticaNode"), reportErrors ("NeticaNode"), initialize
("NeticaNode")
Clique nodes only last for the R session that was used to create
them. After that, they will appear like ordinary nodes. They will
still be present in the network, but the special "clique"
attribute will be lost.
Currently Netica only allows virtual evidence at the node level
(NodeLikelihood()
). I'm lobbying to get Netica to
support it at the clique level as well. At which point, this function
becomes extremely useful.
Russell Almond
Almond, R. G. & Mislevy, R. J. (1999) Graphical models and computerized adaptive testing. Applied Psychological Measurement, 23, 223-238.
Almond, R., Herskovits, E., Mislevy, R. J., & Steinberg, L. S. (1999). Transfer of information between system and evidence models. In Artificial Intelligence and Statistics 99, Proceedings (pp. 181–186). Morgan-Kaufmann
http://norsys.com/onLineAPIManual/index.html:
See the NeticaEx function FormCliqueWith
is the documentation
for JointProbability_bn()
MakeCliqueNode()
, NeticaNode
,
JointProbability()
, AddLink()
,
JunctionTreeReport()
sess <- NeticaSession() startSession(sess) EMSMSystem <- ReadNetworks(system.file("sampleNets","System.dne", package="RNetica"), session=sess) CompileNetwork(EMSMSystem) ## Note that Skill1 and Skill2 are in different cliques JunctionTreeReport(EMSMSystem) Skills12 <- NetworkFindNode(EMSMSystem,c("Skill1","Skill2")) cn <- MakeCliqueNode(Skills12) cnclique <- GetClique(cn) stopifnot( is.CliqueNode(cn), setequal(sapply(cnclique,NodeName),sapply(Skills12,NodeName)) ) CompileNetwork(EMSMSystem) ## Note that Skill1 and Skill2 are in different cliques JunctionTreeReport(EMSMSystem) DeleteNodes(cn) ## This clears the clique. DeleteNetwork(EMSMSystem) stopSession(sess)
sess <- NeticaSession() startSession(sess) EMSMSystem <- ReadNetworks(system.file("sampleNets","System.dne", package="RNetica"), session=sess) CompileNetwork(EMSMSystem) ## Note that Skill1 and Skill2 are in different cliques JunctionTreeReport(EMSMSystem) Skills12 <- NetworkFindNode(EMSMSystem,c("Skill1","Skill2")) cn <- MakeCliqueNode(Skills12) cnclique <- GetClique(cn) stopifnot( is.CliqueNode(cn), setequal(sapply(cnclique,NodeName),sapply(Skills12,NodeName)) ) CompileNetwork(EMSMSystem) ## Note that Skill1 and Skill2 are in different cliques JunctionTreeReport(EMSMSystem) DeleteNodes(cn) ## This clears the clique. DeleteNetwork(EMSMSystem) stopSession(sess)
Before Netica performs inference in a network, it needs to compile the
network. This process consists of building a junction tree and
conditional probability tables for the nodes of that tree. The
function CompileNetwork()
compiles the network and
UncompileNetwork()
undoes the compilation and frees the
associated memory.
CompileNetwork(net) UncompileNetwork(net) is.NetworkCompiled(net)
CompileNetwork(net) UncompileNetwork(net) is.NetworkCompiled(net)
net |
An active |
Usually Bayesian network projects operate in two phases. In the construction phase, new nodes are added to the network, new connections made and conditional probability tables are set.
In the inference phase, findings are added to nodes and other nodes are queried about their current conditional probability tables.
The functions CompileNetowrk()
and UncompileNetwork()
move the networks between the two phases. The documentation for
EliminationOrder()
and JunctionTreeReport()
provide more details about the compilation process. The function
NetworkCompiledSize()
provides information about the
amount of storage used by the compiled network, but only after the
network is compiled.
The function is.NetworkCompiled()
tests to see if a network is
compiled or not.
The NeticaBN
object net
is returned invisibly.
I'm currently observing a bug that occurs under Windows if not all of the nodes have their CPTs set. Under Linux the function exhibits the expected behavior: It generates a warning about the unset CPTs and enters a uniform distribution for each one. Under Windows it reports the warning, but then generates an error "GetError_ns: deleted or damage report_ns passed". It is unclear if this a problem in Netica or RNetica.
To work around, simply set all tables before compiling.
Calling NetworkCompiledSize()
on an uncompiled network
produces, an error, but also the sensible value of -1
. The
function is.NetworkCompiled()
calls the same internal function
as NetworkCompiledSize
, but clears the error. This means it
also clears any other errors that might be lurking in the system (see
ReportErrors()
).
I think calling CompileNetwork()
twice on
the same network is harmless. Adding a node to a network will
automatically uncompile it.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: CompileNet_bn(), UncompileNet_bn(), SizeCompiledNet_bn(),
NeticaBN
, HasNodeTable()
,
NodeFinding()
, NodeBeliefs()
,
EliminationOrder()
, JunctionTreeReport()
,
JointProbability()
,
MostProbableConfig()
,
FindingsProbability()
sess <- NeticaSession() startSession(sess) irt5 <- ReadNetworks(system.file("sampleNets","IRT5.dne", package="RNetica"), session=sess) stopifnot (!is.NetworkCompiled(irt5)) CompileNetwork(irt5) ## Ready to enter findings stopifnot (is.NetworkCompiled(irt5)) UncompileNetwork(irt5) ## Ready to add more nodes stopifnot (!is.NetworkCompiled(irt5)) DeleteNetwork(irt5) stopSession(sess)
sess <- NeticaSession() startSession(sess) irt5 <- ReadNetworks(system.file("sampleNets","IRT5.dne", package="RNetica"), session=sess) stopifnot (!is.NetworkCompiled(irt5)) CompileNetwork(irt5) ## Ready to enter findings stopifnot (is.NetworkCompiled(irt5)) UncompileNetwork(irt5) ## Ready to add more nodes stopifnot (!is.NetworkCompiled(irt5)) DeleteNetwork(irt5) stopSession(sess)
Makes a copy of the networks in the list nets
giving them the
names in newnamelist
. The options
argument controls how
much information is copied.
CopyNetworks(nets, newnamelist, options = character(0))
CopyNetworks(nets, newnamelist, options = character(0))
nets |
A list of |
newnamelist |
A character vector of the same length as |
options |
A character vector containing information about what to copy. The
elements should be one of the values |
Copies each of the networks in the nets
lists, giving it a new
name from the newnamelist
. It returns a list of the new
networks. If the specified net does not exist, then a warning is
issued and a NULL
is returned instead of the corresponding
NeticaBN
object.
The options
argument is passed to the options
argument
of the Netica API function CopyNet_bn()
. Meanings for the
various arguments can be found in the documentation for that
function. Note that Netica expects a list of comma separated values.
RNetica will collapse the options
argument into a comma
separated list, so the argument can be given either as a character
vector of length 1 containing a comma separated list, or the elements
of that list in separate elements of a character vector.
A list of NeticaBN
objects corresponding to the new
networks, or if the length of nets
is one, a single
NeticaBN
object is returned instead. A NULL
is returned
instead of the NeticaBN
object if the corresponding element of
nets
does not exit.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: CopyNet_bn()
sess <- NeticaSession() startSession(sess) net1 <- CreateNetwork("Original", session=sess) nets <- CreateNetwork(paste("Original",2:3,sep=""), session=sess) copy1 <-CopyNetworks(net1,"Copy1") stopifnot(is(copy1,"NeticaBN")) stopifnot(copy1$Name == "Copy1") stopifnot(copy1 != net1) netc <- CopyNetworks(nets,paste("Copy",2:3,sep="")) stopifnot(all(sapply(netc,is,"NeticaBN"))) stopifnot(netc$Name == c("Copy2","Copy3")) DeleteNetwork(c(netc,nets,list(copy1,net1))) stopSession(sess)
sess <- NeticaSession() startSession(sess) net1 <- CreateNetwork("Original", session=sess) nets <- CreateNetwork(paste("Original",2:3,sep=""), session=sess) copy1 <-CopyNetworks(net1,"Copy1") stopifnot(is(copy1,"NeticaBN")) stopifnot(copy1$Name == "Copy1") stopifnot(copy1 != net1) netc <- CopyNetworks(nets,paste("Copy",2:3,sep="")) stopifnot(all(sapply(netc,is,"NeticaBN"))) stopifnot(netc$Name == c("Copy2","Copy3")) DeleteNetwork(c(netc,nets,list(copy1,net1))) stopSession(sess)
This function either copies nodes from one net to another or duplicates nodes within the same network.
CopyNodes(nodes, newnamelist = NULL, newnet = NULL, options = character(0))
CopyNodes(nodes, newnamelist = NULL, newnet = NULL, options = character(0))
nodes |
A list of active |
newnamelist |
If supplied, this should be character vector with the same length as
|
newnet |
If supplied, it should be an active |
options |
A character vector of options, with each element being one of the
options. Currently, the only supported options are
|
The nodes in the first argument will be copied into a new network as
specified by newnet
. If newnet
is not specified or if
it the same as the network from which nodes
come, then the
nodes will be duplicated instead of copied.
If the nodes are duplicated, then will be given new names. The
default Netica behavior for new names is to append a number to the end
of the node name, or to increment an existing number. If
newnamelist
is supplied, these names will be used instead of
the add a number convention. Supplying newnamelist
will change
the names of the nodes when copying from one network to another.
When nodes are copied links going into the node are copied as well.
Thus if there is a link A -> B
in the network and B
is
copied into the same network, then there will a link A -> B1
to the new node. If B
is copied into a new network, the link
will be there but not attached, as if NodeParents(B1)[A]
<- NULL
had been called.
The argument options
allows control over what is copied. The
currently supported options are:
"no_tables"
— The conditional probability tables of
the nodes (see NodeProbs()
) will not be copied, and new
tables will need to be set in the new network.
"no_links"
— The links going into the nodes
will
not be copied. Note that the "no_links"
option implies the
"no_tables"
option, so both do not need to be specified.
A list containing the new nodes (or just the new node, if there is only one).
There may be some information that is not copied. For example, the
NodeSets()
information is not copied.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: CopyNodes_bn()
CopyNetworks()
, NeticaNode
,
NeticaBN()
, NodeProbs()
,
NodeParents()
, AbsorbNodes()
,
DeleteNodes()
sess <- NeticaSession() startSession(sess) System <- ReadNetworks(system.file("sampleNets","System.dne", package="RNetica"), session=sess) EMTask1a <- ReadNetworks(system.file("sampleNets","EMTask1a.dne", package="RNetica"), session=sess) student1 <- CopyNetworks(System, "Student1") student1.sysnodes <- NetworkAllNodes(student1) student1.t1anodes <- CopyNodes(NetworkAllNodes(EMTask1a),newnet=student1) ## Copied, new nodes have the same names as the old nodes. stopifnot( setequal(names(NetworkAllNodes(EMTask1a)), names(student1.t1anodes)) ) ## The nodes in the evidence model have stub connections to the nodes in ## the system model. Need to link them up. stopifnot( any(sapply(NodeParents(student1.t1anodes[[1]]),NodeKind) == "Stub"), any(sapply(NodeParents(student1.t1anodes[[2]]),NodeKind) == "Stub") ) student1.allnodes <- NetworkAllNodes(student1) for (node in student1.t1anodes) { stubs <- sapply(NodeParents(node),NodeKind) == "Stub" NodeParents(node)[stubs] <- student1.allnodes[NodeInputNames(node)[stubs]] } stopifnot( sapply(NodeParents(student1.t1anodes[[1]]),NodeKind) != "Stub", sapply(NodeParents(student1.t1anodes[[2]]),NodeKind) !="Stub" ) ## Duplicate these nodes. student1.t1xnodes <- CopyNodes(student1.t1anodes) ## Autonaming increments the numbers. stopifnot( setequal(names(student1.t1xnodes),c("Obs1a3","Obs1a4")) ) ## Duplicate and rename. student1.t1cnodes <- CopyNodes(student1.t1anodes,c("Obs1c1","Obs1c2")) stopifnot( setequal(names(student1.t1cnodes),c("Obs1c1","Obs1c2")) ) ## Duplicated nodes have real not stub connections. stopifnot( sapply(NodeParents(student1.t1cnodes[[1]]),NodeKind) != "Stub", sapply(NodeParents(student1.t1cnodes[[2]]),NodeKind) !="Stub" ) DeleteNetwork(list(System,student1,EMTask1a)) stopSession(sess)
sess <- NeticaSession() startSession(sess) System <- ReadNetworks(system.file("sampleNets","System.dne", package="RNetica"), session=sess) EMTask1a <- ReadNetworks(system.file("sampleNets","EMTask1a.dne", package="RNetica"), session=sess) student1 <- CopyNetworks(System, "Student1") student1.sysnodes <- NetworkAllNodes(student1) student1.t1anodes <- CopyNodes(NetworkAllNodes(EMTask1a),newnet=student1) ## Copied, new nodes have the same names as the old nodes. stopifnot( setequal(names(NetworkAllNodes(EMTask1a)), names(student1.t1anodes)) ) ## The nodes in the evidence model have stub connections to the nodes in ## the system model. Need to link them up. stopifnot( any(sapply(NodeParents(student1.t1anodes[[1]]),NodeKind) == "Stub"), any(sapply(NodeParents(student1.t1anodes[[2]]),NodeKind) == "Stub") ) student1.allnodes <- NetworkAllNodes(student1) for (node in student1.t1anodes) { stubs <- sapply(NodeParents(node),NodeKind) == "Stub" NodeParents(node)[stubs] <- student1.allnodes[NodeInputNames(node)[stubs]] } stopifnot( sapply(NodeParents(student1.t1anodes[[1]]),NodeKind) != "Stub", sapply(NodeParents(student1.t1anodes[[2]]),NodeKind) !="Stub" ) ## Duplicate these nodes. student1.t1xnodes <- CopyNodes(student1.t1anodes) ## Autonaming increments the numbers. stopifnot( setequal(names(student1.t1xnodes),c("Obs1a3","Obs1a4")) ) ## Duplicate and rename. student1.t1cnodes <- CopyNodes(student1.t1anodes,c("Obs1c1","Obs1c2")) stopifnot( setequal(names(student1.t1cnodes),c("Obs1c1","Obs1c2")) ) ## Duplicated nodes have real not stub connections. stopifnot( sapply(NodeParents(student1.t1cnodes[[1]]),NodeKind) != "Stub", sapply(NodeParents(student1.t1cnodes[[2]]),NodeKind) !="Stub" ) DeleteNetwork(list(System,student1,EMTask1a)) stopSession(sess)
CreateNetwork()
makes a new empty network in Netica, returning
new NeticaBN
objects.
DeleteNetwork()
frees the memory associated with the named
network inside of Netica.
CreateNetwork(names, session=getDefaultSession()) DeleteNetwork(nets)
CreateNetwork(names, session=getDefaultSession()) DeleteNetwork(nets)
names |
A character vector giving the name or names of the network to be created. |
session |
An object of type |
nets |
A list of |
The CreateNetwork
method creates a new network for each of the
names. Names must follow the IDname
rules. It returns
a NeticaBN
object, or a list of such objects if
the argument names
has length greater than 1.
The DeleteNetwork
method frees the Netica memory associated
with each net in its argument. Note that the network will not be
available for use after it is deleted. It returns the
NeticaBN
objects, but modified so that they are no
longer active.
The function is.active()
, checks to see if the network
associated with a NeticaBN
object still corresponds to a
network loaded into Netica's memory.
These functions wrap the Netica API functions NewNet_bn()
and
DeleteNet_bn()
.
A single NeticaBN
object if the length of the
argument is 1, and a list of such objects if the argument has length
greater than 1. For DeleteNets()
if a specified network does
not exist, the corresponding element in the return list will be
NULL
.
In RNetica version 0.5 and later, the NeticaBN
is
used to store the refernce to the network. The enclosing
NeticaSession
object contains a table of network
names to NeticaBN
objects giving the pointer. It will signal
an error if a network with the given name already exists and is active
(not deleted).
In RNetica version 0.4 and prior, the NeticaBN
object used the
name of the networks to store the pointer into the network.
The function DeleteNetwork()
implicitly deletes any nodes
associated with the network. Therefore, any nodes associated with
this network will become inactive (see is.active()
).
Russell Almond
http://norsys.com/onLineAPIManual/index.html: NewNet_bn(), DeleteNet_bn()
NeticaBN
CopyNetworks()
, is.active()
sess <- NeticaSession() startSession(sess) net1 <- CreateNetwork("EmptyNet", session=sess) stopifnot(is(net1,"NeticaBN")) stopifnot(net1$Name=="EmptyNet") stopifnot(is.active(net1)) netd <- DeleteNetwork(net1) stopifnot(!is.active(netd)) stopifnot(!is.active(net1)) stopifnot(netd$Name=="EmptyNet") stopSession(sess)
sess <- NeticaSession() startSession(sess) net1 <- CreateNetwork("EmptyNet", session=sess) stopifnot(is(net1,"NeticaBN")) stopifnot(net1$Name=="EmptyNet") stopifnot(is.active(net1)) netd <- DeleteNetwork(net1) stopifnot(!is.active(netd)) stopifnot(!is.active(net1)) stopifnot(netd$Name=="EmptyNet") stopSession(sess)
This function completely removes the conditional probability table (CPT) associated with a node.
DeleteNodeTable(node)
DeleteNodeTable(node)
node |
An active |
Returns the modified node invisibly.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: DeleteNodeTables_bn()
NeticaNode
, NodeParents()
,
NodeInputNames()
, HasNodeTable()
sess <- NeticaSession() startSession(sess) a1 <- CreateNetwork("AB1", session=sess) A <- NewDiscreteNode(a1,"A",c("A1","A2")) NodeProbs(A) <- c(0,1) stopifnot( all(HasNodeTable(A))==TRUE ) DeleteNodeTable(A) stopifnot( all(HasNodeTable(A))==FALSE ) DeleteNetwork(a1) stopSession(sess)
sess <- NeticaSession() startSession(sess) a1 <- CreateNetwork("AB1", session=sess) A <- NewDiscreteNode(a1,"A",c("A1","A2")) NodeProbs(A) <- c(0,1) stopifnot( all(HasNodeTable(A))==TRUE ) DeleteNodeTable(A) stopifnot( all(HasNodeTable(A))==FALSE ) DeleteNetwork(a1) stopSession(sess)
The function dputToString
converts an R object to a string
which can then be turned back into an R object using
dgetFromString
.
dgetFromString(str) dputToString(obj)
dgetFromString(str) dputToString(obj)
str |
A string containing a serialized object |
obj |
An object to be serialized |
These functions call the base R functions dget
and
dput
using a string buffer as the connection.
Thus, they serialize the R object and return a string value which can
be stored in a NeticaNode
(see
NodeUserObj
) or or NeticaBN
(see
NetworkUserObj
).
Note that the object must be self-contained.
The function dputToString
returns a character scalar containing
the serialized object. Note: Sometimes R “helpfully” adds
line breaks, returning a vector of strings. This can be fixed by
using paste(dputToString(obj),collapse=" ")
.
The function dgetFromString
returns an arbitrary R object
depending on what was stored in str
.
Russell Almond
x <- sample(1L:10L) x1 <- dgetFromString(dputToString(x)) stopifnot(all(x==x1))
x <- sample(1L:10L) x1 <- dgetFromString(dputToString(x)) stopifnot(all(x==x1))
The compilation process involves eliminating the nodes in the network
one-by-one, different orders will produce junction trees of different
sizes. The function EliminationOrder(net)
returns the current
elimination order associated with a network. The expression
EliminationOrder(net) <- value
sets the elimination order.
EliminationOrder(net) EliminationOrder(net) <- value
EliminationOrder(net) EliminationOrder(net) <- value
net |
An active |
value |
Either |
Large cycles create problems for propagating probabilities in Bayesian networks. A solution to this problem is to fill-in chords (short cuts) in the cycles and then transform the network to a tree shape with the nodes of the tree representing cliques of the graph. This is commonly called a junction tree (although a junction tree additionally has nodes separating the cliques, called sepsets in Netica).
Finding the optimal pattern of fill-ins is an NP hard problem. A common way of approaching it is to eliminate the nodes from the network one-by-one and connect the neighbours of the eliminated node (if they were not already connected). In this case, the sequence of eliminated nodes will determine which edges are filled in, and hence the size of the final junction tree. Finding an optimal eliminator order is also NP hard, but simple heuristics (like the greedy algorithm) tend to do reasonably well in practice. (See Almond, 1995, for a complete description of the algorithm and heuristics solutions).
When Netica compiles a network (CompileNetwork(net)
), it
picks an elimination order, unless one has already been set. Unless
the network has a particular difficult structure, then the Netica
defaults should work pretty well. The function
JunctionTreeReport(net)
gives a report about the
existing tree.
If the analyst has some clue about the structure of the network and
wants to manually select the elimination order, this can be set
through the form EliminationOrder(net)<-nodelist
. Here
nodelist
should be a complete list of all of the nodes in
net
with no duplication. Alternatively, it can be set to
NULL
.
Setting the elimination order does not affect an already compiled network, it is only is applied when the network is next compiled.
A list of all of the nodes in the network in elimination order if the
elimination order is currently set, otherwise NULL
.
The setter form returns net
invisibly.
The Netica documentation does not specify the heuristics for selecting the elimination order if no order is specified. I suspect it is some variation on the greedy algorithm, which works well in many cases.
Russell Almond
Almond, R.G. (1995) Graphical Belief Modeling. Chapman and Hall.
http://norsys.com/onLineAPIManual/index.html: GetNetElimOrder_bn(), SetNetElimOrder_bn(),
NeticaBN
, NetworkAllNodes()
,
CompileNetwork()
, JunctionTreeReport()
sess <- NeticaSession() startSession(sess) EMSMMotif <- ReadNetworks(system.file("sampleNets","EMSMMotif.dne", package="RNetica"), session=sess) ## Should be null before we do anything. stopifnot( is.null(EliminationOrder(EMSMMotif)) ) CompileNetwork(EMSMMotif) ## Now should have an elimination order. stopifnot( length(EliminationOrder(EMSMMotif)) == length(NetworkAllNodes(EMSMMotif)), NetworkCompiledSize(EMSMMotif) == 84 ) JunctionTreeReport(EMSMMotif) ## EMSMMotif is partitioned into observable and proficiency variables. ## Tell Netica to eliminate observable variables first. EliminationOrder(EMSMMotif) <- c(NetworkNodesInSet(EMSMMotif,"Observable"), NetworkNodesInSet(EMSMMotif,"Proficiency")) UncompileNetwork(EMSMMotif) CompileNetwork(EMSMMotif) stopifnot( length(EliminationOrder(EMSMMotif)) == length(NetworkAllNodes(EMSMMotif)), NetworkCompiledSize(EMSMMotif) == 84 ) JunctionTreeReport(EMSMMotif) ## Clear elimination order. EliminationOrder(EMSMMotif) <- NULL stopifnot( is.null(EliminationOrder(EMSMMotif)) ) DeleteNetwork(EMSMMotif) stopSession(sess)
sess <- NeticaSession() startSession(sess) EMSMMotif <- ReadNetworks(system.file("sampleNets","EMSMMotif.dne", package="RNetica"), session=sess) ## Should be null before we do anything. stopifnot( is.null(EliminationOrder(EMSMMotif)) ) CompileNetwork(EMSMMotif) ## Now should have an elimination order. stopifnot( length(EliminationOrder(EMSMMotif)) == length(NetworkAllNodes(EMSMMotif)), NetworkCompiledSize(EMSMMotif) == 84 ) JunctionTreeReport(EMSMMotif) ## EMSMMotif is partitioned into observable and proficiency variables. ## Tell Netica to eliminate observable variables first. EliminationOrder(EMSMMotif) <- c(NetworkNodesInSet(EMSMMotif,"Observable"), NetworkNodesInSet(EMSMMotif,"Proficiency")) UncompileNetwork(EMSMMotif) CompileNetwork(EMSMMotif) stopifnot( length(EliminationOrder(EMSMMotif)) == length(NetworkAllNodes(EMSMMotif)), NetworkCompiledSize(EMSMMotif) == 84 ) JunctionTreeReport(EMSMMotif) ## Clear elimination order. EliminationOrder(EMSMMotif) <- NULL stopifnot( is.null(EliminationOrder(EMSMMotif)) ) DeleteNetwork(EMSMMotif) stopSession(sess)
This function takes two arguments, a network and a list of nodes and the corresponding findings. It sets all of the findings at once.
EnterFindings(net, findings)
EnterFindings(net, findings)
net |
An active and compiled |
findings |
An integer or character vector giving the findings. The
|
This function enters findings for multiple nodes at the same time. It
offers two improvements over repeated calls to NodeFinding()
.
First, it finds the nodes by name in the network, making it easier to
work with data in the form of key–value pairs that might come from
other systems. Second, it wraps the calls to NodeFinding()
in
a call to WithoutAutoUpdate()
which should only
propagate the new findings after all values have been entered.
The value of net
is returned invisibly.
Russell Almond
NeticaBN
, NodeBeliefs()
,
EnterNegativeFinding()
, NodeFinding()
,
RetractNodeFinding()
, NodeLikelihood()
,
EnterGaussianFinding()
, EnterIntervalFinding()
,
JointProbability()
, NodeValue()
,
MostProbableConfig()
,
FindingsProbability()
sess <- NeticaSession() startSession(sess) Motif <- ReadNetworks(system.file("sampleNets","EMSMMotif.dne", package="RNetica"), session=sess) CompileNetwork(Motif) obs <- c(Obs1a1="Right",Obs1a2="Wrong", Obs1b1="Right",Obs1b2="Wrong", Obs2a="Half", Obs2b="Half") EnterFindings(Motif,obs) JointProbability(NetworkNodesInSet(Motif,"Proficiency")) DeleteNetwork(Motif) stopSession(sess)
sess <- NeticaSession() startSession(sess) Motif <- ReadNetworks(system.file("sampleNets","EMSMMotif.dne", package="RNetica"), session=sess) CompileNetwork(Motif) obs <- c(Obs1a1="Right",Obs1a2="Wrong", Obs1b1="Right",Obs1b2="Wrong", Obs2a="Half", Obs2b="Half") EnterFindings(Motif,obs) JointProbability(NetworkNodesInSet(Motif,"Proficiency")) DeleteNetwork(Motif) stopSession(sess)
This function a likelihood for a node that follows a Gaussian distribution with a given mean and standard deviation. This is entered as virtual evidence.
EnterGaussianFinding(node, mean, sem, retractFirst = TRUE)
EnterGaussianFinding(node, mean, sem, retractFirst = TRUE)
node |
An active |
mean |
A numeric scalar giving the observed value (mean of the normal). |
sem |
A nonnegative numeric scalar giving the standard error of measurement for the observed finding (standard deviation of the normal). |
retractFirst |
A logical value. If true, any previous findings will be retracted first. |
The node
must a continuous node that has
been discretized using NodeLevels(node)
. The
probabilities for each state are calculated based on a Gaussian
distribution with the given mean
and sem
(SD).
Return the node
argument invisibly.
The Netica function EnterGaussianFinding_bn is not behaving at all
like what I expected. In particular, I expect that it would behave
like a normal likelihood,
but instead it seems to be behaving as if I typed the expression
NodeValue(node)<-mean
. I've queried Norsys about
this.
Meanwhile, I've worked around by calling NodeLikelihood
instead of the internal Netica function.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: EnterGaussianFinding_bn(),
EnterNegativeFinding()
, EnterFindings()
,
RetractNodeFinding()
, NodeLikelihood()
,
NodeFinding()
, EnterIntervalFinding()
,
NodeValue()
sess <- NeticaSession() startSession(sess) cirt5 <- CreateNetwork("ContinuousIRT5", session=sess) theta <- NewContinuousNode(cirt5,"Theta") NodeLevels(theta) <- c(-5,-2.5,-1.5,-0.5,0.5,1.5,2.5,5) NodeProbs(theta) <- rep(1/NodeNumStates(theta),NodeNumStates(theta)) CompileNetwork(cirt5) ## Ready to enter findings EnterGaussianFinding(theta,0,1) NodeBeliefs(theta) stopifnot(all(abs(NodeBeliefs(theta) - diff(pnorm(NodeLevels(theta),0,1))) < .0001)) DeleteNetwork(cirt5) stopSession(sess)
sess <- NeticaSession() startSession(sess) cirt5 <- CreateNetwork("ContinuousIRT5", session=sess) theta <- NewContinuousNode(cirt5,"Theta") NodeLevels(theta) <- c(-5,-2.5,-1.5,-0.5,0.5,1.5,2.5,5) NodeProbs(theta) <- rep(1/NodeNumStates(theta),NodeNumStates(theta)) CompileNetwork(cirt5) ## Ready to enter findings EnterGaussianFinding(theta,0,1) NodeBeliefs(theta) stopifnot(all(abs(NodeBeliefs(theta) - diff(pnorm(NodeLevels(theta),0,1))) < .0001)) DeleteNetwork(cirt5) stopSession(sess)
Sets the finding associate with node to an interval.
EnterIntervalFinding(node, low, high, retractFirst = TRUE)
EnterIntervalFinding(node, low, high, retractFirst = TRUE)
node |
An active |
low |
Lower bound of interval. |
high |
Upper bound of interval. |
retractFirst |
A logical value. If true, any previous findings will be retracted first. |
The node
must a continuous node that has
been discretized using NodeLevels(node)
. The
probabilities for each state are calculated based on a uniform
distribution with the given low
and high
endpoints.
Return the node
argument invisibly.
The internal Netica function EnterIntervalFinding_bn is not behaving
at all like what I expected. In particular, I expect that it would
behave like a uniform likelihood, but instead it seems to be behaving
as if I typed the expression NodeValue(node)<-low
. I've
queried Norsys about this.
Meanwhile, I've worked around by calling NodeLikelihood
instead of the internal Netica function.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: EnterIntervalFinding_bn()
EnterNegativeFinding()
, EnterFindings()
,
RetractNodeFinding()
, NodeLikelihood()
,
NodeFinding()
, EnterGaussianFinding()
,
NodeValue()
sess <- NeticaSession() startSession(sess) cirt5 <- CreateNetwork("ContinuousIRT5", session=sess) theta <- NewContinuousNode(cirt5,"Theta") NodeLevels(theta) <- c(-5,-2.5,-1.5,-0.5,0.5,1.5,2.5,5) NodeProbs(theta) <- rep(1/NodeNumStates(theta),NodeNumStates(theta)) CompileNetwork(cirt5) ## Ready to enter findings EnterIntervalFinding(theta,-1,1) NodeBeliefs(theta) stopifnot(all(abs(NodeBeliefs(theta)*4-c(0,0,1,2,1,0,0))<.0001)) DeleteNetwork(cirt5) stopSession(sess)
sess <- NeticaSession() startSession(sess) cirt5 <- CreateNetwork("ContinuousIRT5", session=sess) theta <- NewContinuousNode(cirt5,"Theta") NodeLevels(theta) <- c(-5,-2.5,-1.5,-0.5,0.5,1.5,2.5,5) NodeProbs(theta) <- rep(1/NodeNumStates(theta),NodeNumStates(theta)) CompileNetwork(cirt5) ## Ready to enter findings EnterIntervalFinding(theta,-1,1) NodeBeliefs(theta) stopifnot(all(abs(NodeBeliefs(theta)*4-c(0,0,1,2,1,0,0))<.0001)) DeleteNetwork(cirt5) stopSession(sess)
This is conceptually equivalent to setting
NodeFinding(node)<-not(eliminatedVals)
(although
this will not work as NodeFinding
does not accept set values).
It essentially eliminates any of the eliminatedVals
as possible
values (assigns them zero probability).
EnterNegativeFinding(node, eliminatedVals)
EnterNegativeFinding(node, eliminatedVals)
node |
An active |
eliminatedVals |
A character or integer vector indicating the values to be ruled out.
Character values should be one of the values in
|
This function essentially asserts that . Thus, it rules out the values in the
eliminatedVals
set. Note that the length of this set should be less than the number
of states, or all possibilities will have been eliminated.
Note calling EngerNegativeFining(node, ...)
clears any previous
findings (including virtual findings set through
NodeLikelihood()
or simple finding set through
NodeFinding(node)<-value
). The function
RetractNodeFinding(node)
will clear the current finding
without setting it to a new value.
This function returns node
invisibly.
If SetNetworkAutoUpdate()
has been set to TRUE
,
then this function could take some time as each finding is
individually propagated. Consider wrapping multiple calls setting
NodeFinding()
in WithoutAutoUpdate(net, ...)
.
Unlike the Netica function EnterFindingNot_bn()
the function
EnterNegativeFinding()
internally calls RetractFindings
. So
there is no need to do this manually. Also, the internal Netica
function multiplies multiple calls to EnterFindingNod_bn()
add
to the list of negative findings, while in the R version takes the
entire list.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: EnterFindingNot_bn()
NeticaBN
, NodeBeliefs()
,
NodeFinding()
,
RetractNodeFinding()
, NodeLikelihood()
sess <- NeticaSession() startSession(sess) irt5 <- ReadNetworks(system.file("sampleNets","IRT5.dne", package="RNetica"), session=sess) irt5.theta <- NetworkFindNode(irt5,"Theta") irt5.x <- NetworkFindNode(irt5,paste("Item",1:5,sep="_")) CompileNetwork(irt5) ## Ready to enter findings ## Calculated new expected beliefs renormed <- NodeProbs(irt5.theta) renormed[c("neg1","neg2")] <- 0 renormed <- renormed/sum(renormed) ## Negative finding EnterNegativeFinding(irt5.theta,c("neg1","neg2")) ## Rule out negatives. stopifnot( NodeFinding(irt5.theta) == "@NEGATIVE FINDINGS", sum(abs(NodeLikelihood(irt5.theta) - c(1,1,1,0,0))) < 1e-6, sum(abs(NodeBeliefs(irt5.theta) - renormed)) < 1.e-6 ) DeleteNetwork(irt5) stopSession(sess)
sess <- NeticaSession() startSession(sess) irt5 <- ReadNetworks(system.file("sampleNets","IRT5.dne", package="RNetica"), session=sess) irt5.theta <- NetworkFindNode(irt5,"Theta") irt5.x <- NetworkFindNode(irt5,paste("Item",1:5,sep="_")) CompileNetwork(irt5) ## Ready to enter findings ## Calculated new expected beliefs renormed <- NodeProbs(irt5.theta) renormed[c("neg1","neg2")] <- 0 renormed <- renormed/sum(renormed) ## Negative finding EnterNegativeFinding(irt5.theta,c("neg1","neg2")) ## Rule out negatives. stopifnot( NodeFinding(irt5.theta) == "@NEGATIVE FINDINGS", sum(abs(NodeLikelihood(irt5.theta) - c(1,1,1,0,0))) < 1e-6, sum(abs(NodeBeliefs(irt5.theta) - renormed)) < 1.e-6 ) DeleteNetwork(irt5) stopSession(sess)
Provides an efficient mechanism for extracting or setting portions of
large conditional probability tables. In particular, allows setting
many rows a CPT to the same value. The node[]
form is for
chance (probabilistic) nodes, the node[[]]
form is for
deterministic (functional) nodes. See
IsNodeDeterministic
.
## S4 method for signature 'NeticaNode' x[i, j,..., drop=FALSE] ## S4 method for signature 'NeticaNode' x[[i, j, ..., drop=FALSE]] ## S4 replacement method for signature 'NeticaNode' x[i, j, ...] <- value ## S4 replacement method for signature 'NeticaNode' x[[i, j, ...]] <- value EVERY_STATE
## S4 method for signature 'NeticaNode' x[i, j,..., drop=FALSE] ## S4 method for signature 'NeticaNode' x[[i, j, ..., drop=FALSE]] ## S4 replacement method for signature 'NeticaNode' x[i, j, ...] <- value ## S4 replacement method for signature 'NeticaNode' x[[i, j, ...]] <- value EVERY_STATE
x |
An active, discrete |
i , j , ...
|
Indices specifying rows of the table to extract or
replace. If a single index, |
drop |
If true and a single row is selected, that row will be returned as a
numeric vector instead of a conditional probability frame
( |
value |
Either a numeric vector with length |
The function NodeProbs(node)
allows one to access the
entire conditional probability at once as a conditional probability
array (CPA
). Although the built-in R array replacement
mechanisms allow one to make various kinds of edits, it is relatively
inefficient. In particular, to set a single row of an array, the
entire table is read into R and then written back to Netica.
This function allows the syntax node[...]
to be used to
access only a portion of the table. There are many different ways
...
can be interpreted, which are described below.
In this access model the value EVERY_STATE
or the character
value "*"
has a special meaning of match every level of that
state variable. Netica supports this as a shortcut method for
specifying conditional probability tables with many similar
values. However, when reading the conditional probability tables from
Netica they are expanded and no attempt is made to collapse over
identical rows.
A second difference is that node[...]
returns the
conditional probability table in data frame (CPF
)
format. This is particularly convenient because that format does not
need to cover every parent configuration, thus it is ideal for holding
subset of the complete table.
A third difference is that if the last column of the conditional probabilities is not supplied, it will be computed. This is particularly handy for binary nodes.
Normally, the expression node[...]
produces a data frame
either in CPF
format, or with the probabilities replaced
by a single column of values. If drop==TRUE
, only the matrix of
probabilities or the vector of values will be returned. See also
numericPart
.
Deterministic nodes (see IsNodeDeterministic
) should be
accessed using the node[[...]]
form. In this form, the node
has a value table which maps a configuration of the parent values to a
value of the node. That will be a numeric value for continuous
nodes, and a factor for discrete variables. Note that Netica figures
out whether or not a node is deterministic on the fly. For that
reason, it is strongly recommended to use node[[...]]
to access
the value table, and node[...]
to access the CPT.
In using the form node[[...]] <- value
the value depends on
whether the node is continuous or discrete. For continuous nodes, the
node's value for a parent configuration (assuming all discrete or
discretized parents) can be set directly if value
is numeric.
(If value
is a factor or a string, it behaves like a discrete
node.). For a discrete node, value
can be a factor, string or
integer, incidating the state. This creates a deterministic conditional
probability table full of 1's and 0's.
The sections below describe the various indexing options.
For the form node[...]
the return value is a data frame in
the CPF
format giving the conditional probability
table.
For the form node[[...]]
, if the node is deterministic
(IsNodeDetermistic(node)==TRUE
) then the probabilities
will be replaced with a single column giving the value of the node.
If the node is discrete, then the value will be a factor. If the node
is continuous, then the value will be a real vector.
If drop==TRUE
then the return value will be a
matrix of probabilities (the last several columns of the data frame).
If the node is deterministic, then the result will instead be either a
factor (discrete node) or real vector (continuous node) giving the
value of the node for each parent configuration.
The forms node[...]<-value
and node[[...]]<-value
return node
invisibly.
This selection uses the syntax node[df]
or
node[df]<-value
, where df
is a data frame or a matrix.
It is assumed that the columns represent the variables, and the rows
represent the selected configurations of the parent variables.
In this configuration, the number of rows of df
and
value
should match (or the length of value
should equal
the number of rows if one of the special values is used). When the
value is being queried rather than set, the number of rows in the
result may be greater than the number of rows in df
because of
EVERY_STATE
expansion.
There are three different ways that df
could be represented:
It can be a data frame filled with factor variables whose levels correspond to the states of the corresponding parent node.
It can be a matrix or data frame of type character whose
values correspond to the state names of the corresponding parent
variables, or possibly the special value "*"
meaning that all
values of that parent should be matched.
It can be a matrix of data frame of integers whose values
correspond to the state indexes of the parent variables. In this case
the special value EVERY_STATE
can be supplied indicating that
all values should be matched. Otherwise, it should be a number
between 1 and the number of states of that variable, inclusive.
The number of columns in df
should be the same as the number of
parent variables for node
. If df
has column names, then
all columns should be named. In this case the parent variables will
be match by the NodeInputNames(node)
if they exist, or
the names of the parent variables if they do not (see
ParentStates(node)
for more details). Otherwise,
positional selection is used.
The second way that rows from the conditional probability table can be
selected is using an analogue of the selection mechanisms supported by
R for selecting cells from an array. Essentially, the rows of the
conditional probability table are treated as if they are the elements
of an array whose dimnames correspond to
ParentStates{node}
. In particular the number of
dimensions corresponds to the number of parent variables, and the
extent of each dimension corresponds to the number of states of the
corresponding parent variable.
In this selection mode, the length of ...
should correspond to
the number of parent variables (that is, there should be one fewer
comma, than parent variables). Each element can be one of three
things:
A character or factor vector selecting the appropriate states of the parent variable.
An integer vector selecting the appropriate states of the parent variable by position.
One of the special values EVERY_STATE
, "*"
or
blank indicating that all values of the appropriate variable should be
selected.
The order of the entries should be the same as the order of the parent
variables in NodeParents{node}
. The selection looks
very similar to selection using a data frame, where the data frame
consists of applying expand.grid(...)
.
Once again EVERY_STATE
or "*"
entries are treated
specially inside of Netica, which allows every matching row of the
table to be simultaneously set to the same probabilities.
Note that negative selections and logical selections are not currently supported.
As with R array index selection, the dimensions of the selection in
the ...
argument can be specified using named arguments. If
one of the elements of ...
is named, they all should be
named. The names should correspond to
ParentNames(node)
, that is the
NodeInputNames(node)
are used if available, and the
names of the parent nodes are used as a fallback.
As before the value for a parent variable can be set to a value or a
vector of possible values as either an integer, factor or character
value. The special values EVERY_STATE
and "*"
are
interpreted as before. If the value of a parent variable is
unspecified, this is equivalent to using the value
EVERY_STATE
.
If ...
is a single integer, it is treated as an index into the
possible configurations. These are defined by
expand.grid(ParentStates(node)
. Each index
refers to a row in that table. This is particularly meant for running
through loops on all values, although working with value as a data
frame or using NodeProbs
may be faster in those cases.
There is some ambiguity when there is a single parent variable about whether the array-type selection or the index was intended, but both are identical, so there should be no conflict.
NULL
selectionIf ...
is NULL
, that is if the calling expression looks
like node[]
then the intention is that all rows of the
conditional probability table are to be selected. This is the only
meaningful selection type if there are no parent variables. It also
provides a fast and convenient way to set all rows of the conditional
probability table to the same value (if value
) has a single
row, or to retrieve the complete conditional probability table in
CPF
format.
If value
is a data frame with both factor and numeric
variables, then it takes on a different meaning. In this case, the
factor variables are used as if they were the selection argument (the
...
) and the remaining numeric values the probabilities.
In general the replacement value should be a matrix. The number of
columns should match the number of states of node
(see below
for the behavior if the number of columns is one less than the number
of states). It should have the same number of rows as the number of
rows in the selection after any expansion has been applied for vector
valued arguments, but not counting the special values
EVERY_STATE
or "*"
(or blank entries in the list).
Netica has a special shortcut for EVERY_STATE
and all matching
rows are set to the same probability value. This means that the number
of rows in the value must match the selection counting the special
values as if they selected a single row. In particular, if
node
has one or more parent variables and value
is a
matrix with more than one row, node[] <- value
will generate a
error, because the selection has only one row (with every value set to
EVERY_STATE
).
When value
is an undimensioned vector, the function will do its
best to figure out if it should be treated as a row or a column
vector. In the case of unusual behavior, expressing value
as a
matrix should make the programmer's intention clear.
When a node is deterministic, that is all probabilities are or
, then it is meaningful to talk about the conditional value of
a node instead of the conditional probability table. The expression
node[[...]]
displays the conditional probability table in a
special way when the node is deterministic. In this case it displays
the value as a single variable giving the state of the child variable
given the configuration of the parents. In the case of discrete
nodes, this is a factor variable giving the state. In the case of
continuous nodes, this is a numeric vector giving the value.
The same conventions can be used in setting the conditional
probability of a node. In the expression node[[...]] <- value
if value
is a factor or character vector then the selected
configurations are set to deterministic probabilities with the
indicated value given probability of and all others with
probability
. It is possible to set some rows of a conditional
probability table to be deterministic and others to have unrestricted
probabilities, however, the deterministic rows will then print out as
unconstrained probabilities with
and
values.
Continuous nodes (nodes for which is.continuous(node) ==
TRUE
) use a variation of this system. Here the value is an arbitrary
numeric value. For this to be meaningful, it is assumed that all of
the parents of node
are either discrete or have been
discretized. The use of the node[[...]] <- value
is
particularly important for continuous nodes becuase it indicates that
value is a potential value of the node rather than a
probability.
Warning: Setting an unconditional discrete node to a constant value,
that is executing an expression like node[[]] <- value
is almost certainly a mistake. Probably what is intended by that
expression is NodeFinding(node) <- value
. In
particular, if the former expression is used and the later someone
attempts to set NodeFinding(node) <- value1
, where
value1 != value
, this will produce a contradiction
(probability zero event) and all kinds of error will follow.
If the number of columns in value
is one less than the number of
states in node
, then is assumed that the probability values
should be calculated for the last state via normalization, that is it
is assigned all of the remaining probability not assigned in the first
couple of columns. In particular, the value is internally translated
via the expression:
value <- cbind(value,1-apply(value,1,sum))
.
This is particularly useful when the node is binary (has exactly 2
states). Then the replacement only needs to specify the probability
for the first one. For example node[] <- .5
would set the
probability distribution of node
to the uniform distribution if
node
is binary.
There is some potential for confusion if value
is not specified
as a matrix. In particular, if the number of states of the child
value is one more than the number of configurations of the parents, it
is unclear whether this is an attempt to set the node value of a
discrete node or an unnormalized probability. It should be possible
by specifying value as a matrix or one row or one column to clarify
the intent.
RNetica version 0.7 changed the ways that the node[...]
and
node[[...]]
were handled, establishing that the former is
for manipulating conditional probability tables, and the latter for
manipulating value tables for deterministic nodes.
The return value of node[]
for a deterministic
node node has changed This now returns the conditional probability
table. Use node[[]]
to get the value table.
The use of node[[...]]
as a synnonym for
node[...,drop=TRUE]
is deprecated. (I'm not sure it was
working correctly).
The use of node[...]<- value
to set rows in a function table
for a discrete node is deprecated. Use node[[...]]<- value
instead.
An error where node[...]<- value
always treated continuous
nodes as deterministic is fixed. (Note that continuous nodes cannot
be treated as random unless they have been discretized.)
I have tried to anticipate most of the ways that somebody might want to index the conditional probability table, not to mention all of the peculiar ways that R overloads the extraction operator. Negative selections are not allowed. I have almost certainly missed some combinations, and some untested combinations might preform rather strangely. Undoubtedly somebody will come to rely on that strangeness and it will never get fixed.
Factor variables do not easily handle the use of "*"
as a
wildcard. To make this work, a construction like
factor(varstates, c(1:3,EVERY_STATE),
labels=c("a1","a2","a3","*"))
.
Internally R uses 1-based indexing and Netica uses 0-based indexing. RNetica makes the translation inside of the C layer, so these function should be called with R-style 1-based indexing.
I'm having weird race conditions when trying to set the value of
EVERY_STATE
(I can't figure out how to call the C function to
set its value after the C code is loaded but before the namespace is
exported. So for now the exported EVERY_STATE
is different
from the internal Netica value (which is RNetica:::EVERY_STATE
,
at least in the current implementation). This should not be a visible
change to the user.
This documentation file is longer than War and Peace.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNodeProbs_bn(), SetNodeProbs_bn(), GetNodeFuncState_bn(), SetNodeFuncState_bn(), GetNodeFuncReal_bn(), SetNodeFuncReal_bn(),
NeticaNode
, NodeParents()
,
NodeInputNames()
, NodeStates()
,
ParentStates()
, CPF
, CPA
,
IsNodeDeterministic
## Setup sess <- NeticaSession() startSession(sess) xnet <- CreateNetwork("X", session=sess) A <- NewDiscreteNode(xnet,"A",c("A1","A2","A3","A4")) Aalt <- NewDiscreteNode(xnet,"Aalt",c("A1","A2","A3","A4")) B <- NewDiscreteNode(xnet,"B",c("B1","B2","B3")) B2 <- NewDiscreteNode(xnet,"B2",c("B1","B2")) Balt <- NewDiscreteNode(xnet,"Balt",c("B1","B2","B3")) C2 <- NewDiscreteNode(xnet,"C2",c("C1","C2")) C3 <- NewDiscreteNode(xnet,"C3",c("C1","C2","C3")) C4 <- NewDiscreteNode(xnet,"C4",c("C1","C2","C3","C4")) Cont <- NewContinuousNode(xnet,"Cont") CC <- NewContinuousNode(xnet,"CC") CCC <- NewContinuousNode(xnet,"CCC") ### Tests for various setting modes. ## Null before we set any probabilities anything stopifnot( all(is.na(C2[])), length(C2[]) == 2, all(is.na(Cont[])), length(Cont[])==1 ) NodeProbs(C2) <- c(1,0) stopifnot( C2[[]]=="C1" ) ## This is just a demonstration of the syntax, in practice ## the expression NodeFinding(C2) <- "C2" is usually better. C2[[]] <- "C2" stopifnot( NodeProbs(C2)==c(0,1) ) C3[[]] <- 3 stopifnot( C3[[]] == "C3" ) ## Setting value of continuous node Cont[[]] <- 145.4 stopifnot( abs(Cont[[]] - 145.4) < .0001) ## Setting value with probabilities C2[] <- c(.3,.7) stopifnot( sum(abs(NodeProbs(C2)-c(.3,.7))) < .0001) C3[] <- c(1,2,1)/4 stopifnot( sum(abs(NodeProbs(C3)-c(.25,.5,.25))) < .0001) ## Automatic normalization C2[] <- .25 stopifnot( abs(sum(NodeProbs(C2)-c(.25,.75))) < .0001) C3[] <- c(1,1)/3 stopifnot( abs(sum(NodeProbs(C3)-1/3)) < .0001) ### Now some one parent cases AddLink(A,B) AddLink(A,B2) stopifnot( nrow(B[])==NodeNumStates(A), ncol(B[])==1+NodeNumStates(B), all(is.na(B[][,2:(1+NodeNumStates(B))])) ) NodeProbs(B) <- normalize(matrix(1:12,4)) Brow1 <- B[1] stopifnot( nrow(Brow1)==1,ncol(Brow1)==4, sum(abs(Brow1[,2:4]-c(1,5,9)/15))<.00001 ) Brow12 <- B[1:2] stopifnot( nrow(Brow12)==2,ncol(Brow12)==4, sum(abs(Brow12[2,2:4]-c(2,6,10)/18))<.00001 ) Brow4 <- B["A4"] stopifnot( nrow(Brow4)==1,ncol(Brow4)==4, sum(abs(Brow4[,2:4]-c(1,2,3)/6))<.00001 ) Brow34 <- B[c("A3","A4")] stopifnot( nrow(Brow34)==2,ncol(Brow34)==4, abs(sum(Brow4[1,2:4]-c(3,7,11)/21))<.00001 ) Ball <- B["*"] stopifnot( nrow(Ball)==4,ncol(Ball)==4 ) Ball <- B[EVERY_STATE] stopifnot( nrow(Ball)==4,ncol(Ball)==4 ) Brow24 <- B[data.frame(A=factor(c("A2","A4"),NodeStates(A)))] stopifnot( nrow(Brow24)==2,ncol(Brow24)==4, sum(abs(Brow24[2,2:4]-c(1,2,3)/6))<.00001 ) ## Set all rows to the same value. B[] <- matrix(c(1,1,1)/3,1) stopifnot( abs(NodeProbs(B)-1/3)<.0001 ) B[EVERY_STATE] <- matrix(c(1,2,1)/4,1) stopifnot( abs(NodeProbs(B)[3,]-c(.25,.5,.25))<.0001 ) B["*"] <- matrix(c(1,2,3)/6,1) stopifnot( abs(NodeProbs(B)[2,]-c(1/6,1/3,.5))<.0001 ) ## Setting to exact values B2[[1:2]] <- "B1" B2[[3]] <- "B2" B2[[4]] <- "B2" B2tab <- B2[[]] stopifnot( IsNodeDeterministic(B2), nrow(B2tab)==4,ncol(B2tab)==2, B2tab[,2] == c("B1","B1","B2","B2"), as.integer(B2tab[,2]) == c(1,1,2,2) ) ## Setting one value to non-deterministic changes the way the table is ## displayed. B2[2] <- c(.5,.5) B2tab <- B2[] stopifnot( !IsNodeDeterministic(B2), nrow(B2tab)==4,ncol(B2tab)==3, sum(abs(B2tab[2,2:3]- c(.5,.5))) < .001, B2tab[1,2:3] == c(1,0), B2[3,drop=TRUE] == c(0,1) ) ## Self-normalizing setting ## Not run: ## This will generate an error because it is trying to set all four ## configurations to the same value but it is given four values. B2[] <- c(.1,.2,.3,.4) ## End(Not run) B2[1:4] <- c(.1,.2,.3,.4) stopifnot( sum(abs(NodeProbs(B2)[,2]-c(.9,.8,.7,.6))) < .001 ) B2[1:2] <- .5 ## Set both values to the same thing B2[3:4] <- c(.6,.7) ## set to normalizing probs stopifnot( sum(abs(NodeProbs(B2)[,2]-c(.5,.5,.4,.3))) < .001 ) ## Beware! This next form assumes you are setting the rows to the same ## thing. B2[3:4] <- c(.2,.8) ## Ambiguous instructions stopifnot( sum(abs(NodeProbs(B2)[,2]-c(.5,.5,.8,.8))) < .001 ) ## Using a matrix makes intent clear B2[3:4] <- matrix(c(.2,.8),2) ## set to normalizing probs stopifnot( sum(abs(NodeProbs(B2)[,2]-c(.5,.5,.8,.2))) < .001 ) ## Data frame as value ## First do a blank extraction to get general shape. B2frame <- B2[] ## Now manipulate it however B2frame[,2:3] <- 1:8 ## And set it back B2[] <- normalize(B2frame) stopifnot( sum(abs(NodeProbs(B2)[,1]-c(1/6,2/8,3/10,4/12))) <.001 ) B2frame1 <-B2frame[B2frame$A=="A3",] B2frame1[,2:3] <- c(4,6)/10 B2[] <- B2frame1 ## Only row 3 affected stopifnot( sum(abs(NodeProbs(B2)[,1]-c(1/6,2/8,4/10,4/12))) <.001 ) ## Continuous node with one discrete parent AddLink(A,Cont) ##Notice how old value is replicated stopifnot( nrow(Cont[[]]) ==4, ncol(Cont[[]]) == 2, abs(Cont[[]][,2]-145.4) <.0001, abs(Cont[[3,drop=TRUE]]-145.4) <.0001 ) AddLink(A,CC) stopifnot( nrow(CC[]) ==4, ncol(CC[]) == 2, is.na(CC[][,2]) ) Cont[[]] <- 7 stopifnot( abs(Cont[[,drop=TRUE]]-7) <.0001 ) Cont[[2]] <- 3.2 stopifnot( abs(Cont[[,drop=TRUE]]-c(7,3.2,7,7)) <.0001 ) Cont[[1:2]] <- 0 Cont[[3:4]] <- c(8,1) stopifnot( abs(Cont[[,drop=TRUE]]-c(0,0,8,1)) <.0001, abs(Cont[[3:4,drop=TRUE]]-c(8,1)) < .0001 ) ## Two parent case AddLink(A,C2) AddLink(B,C2) C2[] <- c(.5,.5) stopifnot( nrow(C2[])==12, ncol(C2[])==4, sum(abs(numericPart(C2[])-.5)) < .0001 ) AddLink(A,C4) AddLink(B,C4) stopifnot( nrow(C4[])==12, ncol(C4[])==6, all(is.na(C4[,drop=TRUE])) ) NodeProbs(C4) <- normalize(array(1:48,c(4,3,4))) ## Data Frame/matrix Selection dfsel <- data.frame(A=factor(c("A2","A3"),levels=NodeStates(A)), B=factor(c("B1","B3"),levels=NodeStates(B))) C21.33 <- C4[dfsel] stopifnot( nrow(C21.33)==2, ncol(C21.33)==6, C21.33[1,1] == "A2", C21.33[2,2] == "B3", abs(C21.33[1,3]-2/80) < .0001, abs(C21.33[2,4]-23/116) < .0001 ) dfselbak <- data.frame(B=factor(c("B3","B2"),levels=NodeStates(B)), A=factor(c("A1","A4"),levels=NodeStates(A))) C13.42 <- C4[dfselbak] stopifnot( nrow(C13.42)==2, ncol(C13.42)==6, C13.42[1,1] == "A1", C13.42[2,2] == "B2", abs(C13.42[1,3]-9/108) < .0001, abs(C13.42[2,4]-20/104) < .0001 ) C2[dfsel] <- matrix(c(.7,.6,.3,.4),2) C2[dfselbak] <- c(.9,.1) stopifnot( sum(abs(numericPart(C2[])[,1] - c(.5,.7,.5,.5, .5,.5,.5,.9, .9,.5,.6,.5))) < .0001 ) ## Test for error with using variables in selection inside of a ## function. testSel <- function(node,sel1,sel2, val) { localselvar <- data.frame(sel1,sel2) names(localselvar) <- ParentNames(node) node[localselvar] node[localselvar]<-val invisible(node) } testSel(C2,factor(c("A2","A3"),levels=NodeStates(A)), factor(c("B1","B3"),levels=NodeStates(B)), matrix(c(.7,.6,.3,.4),2)) ## Array-like selection stopifnot( sum(abs(numericPart(C4[2,3])-c(10,22,34,46)/112))<.0001, sum(abs(numericPart(C4[B=2,A=4])-c(8,20,32,44)/104))<.0001 ) C1.23 <- C4[1,2:3] stopifnot( nrow(C1.23)==2, ncol(C1.23)==6, sum(abs(C1.23[,3] - c(5/92 ,9/108))) <.0001 ) C2[] <- .5 C2[1,2:3] <- .99 stopifnot( sum(abs(numericPart(C2[])[,1] - c(.5,.5,.5,.5, .99,.5,.5,.5, .99,.5,.5,.5))) < .0001 ) C1.23 <- C4["A1",c("B2","B3")] stopifnot( nrow(C1.23)==2, ncol(C1.23)==6, sum(abs(C1.23[,3] - c(5/92 ,9/108))) <.0001 ) C2[] <- .5 C2["A1",c("B2","B3")] <- .99 stopifnot( sum(abs(C2[,drop=TRUE][,1] - c(.5,.5,.5,.5, .99,.5,.5,.5, .99,.5,.5,.5))) < .0001 ) C34.12 <- C4[3:4,1:2] stopifnot( nrow(C34.12)==4, ncol(C34.12)==6, sum(abs(C34.12[,3] - c(3/84,4/88, 7/100, 8/104))) <.0001 ) C2[] <- .5 C2[3:4,1:2] <- .99 stopifnot( sum(abs(C2[][,3] - c(.5,.5,.99,.99, .5,.5,.99,.99, .5,.5,.5,.5))) < .0001 ) ## Wildcards C1. <- C4[1,EVERY_STATE] stopifnot( nrow(C1.) == 3, ncol(C1.)==6, sum(abs(C1.[,3] -c(1/76, 5/92, 9/108))) < .0001 ) C2[] <-.5 C2[1,EVERY_STATE] <- "C1" stopifnot( sum(abs(C2[][,3] - c(1,.5,.5,.5, 1,.5,.5,.5, 1,.5,.5,.5))) < .0001 ) C.2 <- C4[EVERY_STATE,2] stopifnot( nrow(C.2) == 4, ncol(C.2)==6, sum(abs(C.2[,3] -c(5/92, 6/96, 7/100, 8/104))) < .0001 ) C2[] <-.5 C2[[EVERY_STATE,2]] <- "C2" stopifnot( sum(abs(C2[drop=TRUE][,1] - c(.5,.5,.5,.5, 0,0,0,0, .5,.5,.5,.5))) < .0001 ) C1. <- C4["A1","*"] stopifnot( nrow(C1.) == 3, ncol(C1.)==6, sum(abs(C1.[,3] -c(1/76, 5/92, 9/108))) < .0001 ) C2[] <-.5 C2[["A1","*"]] <- "C1" stopifnot( sum(abs(C2[drop=TRUE][,1] - c(1,.5,.5,.5, 1,.5,.5,.5, 1,.5,.5,.5))) < .0001 ) C.2 <- C4["*","B2"] stopifnot( nrow(C.2) == 4, ncol(C.2)==6, sum(abs(C.2[,3] -c(5/92, 6/96, 7/100, 8/104))) < .0001 ) C2[] <-.5 C2[["*","B2"]] <- "C2" stopifnot( sum(abs(C2[drop=TRUE][,1] - c(.5,.5,.5,.5, 0,0,0,0, .5,.5,.5,.5))) < .0001 ) ## Missing parent values C1. <- C4[1,] stopifnot( nrow(C1.) == 3, ncol(C1.)==6, sum(abs(C1.[,3] -c(1/76, 5/92, 9/108))) < .0001 ) C2[] <-.5 C2[[1,]] <- "C1" stopifnot( sum(abs(C2[drop=TRUE][,1] - c(1,.5,.5,.5, 1,.5,.5,.5, 1,.5,.5,.5))) < .0001 ) C.2 <- C4[,2] stopifnot( nrow(C.2) == 4, ncol(C.2)==6, sum(abs(C.2[,3] -c(5/92, 6/96, 7/100, 8/104))) < .0001 ) C2[] <-.5 C2[[,2]] <- "C2" stopifnot( sum(abs(C2[drop=TRUE][,1] - c(.5,.5,.5,.5, 0,0,0,0, .5,.5,.5,.5))) < .0001 ) C1. <- C4[A=1] stopifnot( nrow(C1.) == 3, ncol(C1.)==6, sum(abs(C1.[,3] -c(1/76, 5/92, 9/108))) < .0001 ) C2[] <-.5 C2[[A=1]] <- "C1" stopifnot( sum(abs(C2[drop=TRUE][,1] - c(1,.5,.5,.5, 1,.5,.5,.5, 1,.5,.5,.5))) < .0001 ) C.2 <- C4[B="B2"] stopifnot( nrow(C.2) == 4, ncol(C.2)==6, sum(abs(C.2[,3] -c(5/92, 6/96, 7/100, 8/104))) < .0001 ) C2[] <-.5 C2[[B="B2"]] <- "C2" stopifnot( sum(abs(C2[drop=TRUE][,1] - c(.5,.5,.5,.5, 0,0,0,0, .5,.5,.5,.5))) < .0001 ) ## Data frame as value dfset <- data.frame(A=factor(c("A2","A3"),levels=NodeStates(A)), B=factor(c("B1","B3"),levels=NodeStates(B)), C.C1=c(1,0), C.C2=c(0,1)) C2[] <- .5 C2[] <- dfset stopifnot( sum(abs(C2[][,3] - c(.5,1,.5,.5, .5,.5,.5,.5, .5,.5,0,.5))) < .0001 ) ## Continuous Child node AddLink(B2,Cont) stopifnot( nrow(Cont[[]])==8, ncol(Cont[[]])==3, sum(abs(Cont[[drop=TRUE]]-c(0,0,8,1))) < .0001 ) AddLink(A,CCC) AddLink(B,CCC) stopifnot( nrow(CCC[])==12, ncol(CCC[])==3, all(is.na(CCC[][,3])) ) Cont[[]] <- 0 Cont[[1,1]] <- 1.1 Cont[[2:3,2]] <- c(2.2,3.2) Cont[["A4","*"]] <- 4 ## Not run: ## Can't set to multiple values when using * selection. Cont[["A4","*"]] <- c(4.1,4.2) ## Generates an error ## End(Not run) stopifnot( sum(abs(Cont[[drop=TRUE]]-c(1.1,0,0,4,0,2.2,3.2,4))) < .0001, abs(Cont[["A1","B1",drop=TRUE]]-1.1) <.0001, sum(abs(Cont[[B=2,A=2:3,drop=TRUE]]-c(2.2,3.2))) < .0001, sum(abs(Cont[[A=4,drop=TRUE]] -4)) < .0001 ) ## Set by integer count ## 12 rows in A*B combinations for (i in 1:12) { CCC[[i]] <- i C2[i] <- i/100 } stopifnot( sum(abs(CCC[[drop=TRUE]]-t(matrix(1:12,3,4)))) <.0001, sum(abs(C2[drop=TRUE][,1]-t(matrix(1:12/100,3,4)))) <.001 ) for (i in 1:12) { stopifnot( abs(CCC[[i,drop=TRUE]] - i) <.0001, abs(C2[i,drop=TRUE][1] - i/100) <.0001 ) } ### Try some things with three parents, just to make sure that works ### too. C2tab <- C2[drop=TRUE] AddLink(C3,C2) C2.1tab <- C2[,,"C1",drop=TRUE] stopifnot(all.equal(C2tab,C2.1tab), all.equal(C2tab,C2[,,"C1",drop=TRUE]), all.equal(C2tab,C2[C="C3",drop=TRUE])) stopifnot(all(abs(C2["A1","B1","C1",drop=TRUE]-NodeProbs(C2)[1,1,1,])<.0001), all.equal(C2["A1",,],C2[A="A1"]), all.equal(C2[,"B2",],C2[B="B2"]), all.equal(C2["A1","B2",],C2[B="B2",A="A1"])) DeleteNetwork(xnet) stopSession(sess)
## Setup sess <- NeticaSession() startSession(sess) xnet <- CreateNetwork("X", session=sess) A <- NewDiscreteNode(xnet,"A",c("A1","A2","A3","A4")) Aalt <- NewDiscreteNode(xnet,"Aalt",c("A1","A2","A3","A4")) B <- NewDiscreteNode(xnet,"B",c("B1","B2","B3")) B2 <- NewDiscreteNode(xnet,"B2",c("B1","B2")) Balt <- NewDiscreteNode(xnet,"Balt",c("B1","B2","B3")) C2 <- NewDiscreteNode(xnet,"C2",c("C1","C2")) C3 <- NewDiscreteNode(xnet,"C3",c("C1","C2","C3")) C4 <- NewDiscreteNode(xnet,"C4",c("C1","C2","C3","C4")) Cont <- NewContinuousNode(xnet,"Cont") CC <- NewContinuousNode(xnet,"CC") CCC <- NewContinuousNode(xnet,"CCC") ### Tests for various setting modes. ## Null before we set any probabilities anything stopifnot( all(is.na(C2[])), length(C2[]) == 2, all(is.na(Cont[])), length(Cont[])==1 ) NodeProbs(C2) <- c(1,0) stopifnot( C2[[]]=="C1" ) ## This is just a demonstration of the syntax, in practice ## the expression NodeFinding(C2) <- "C2" is usually better. C2[[]] <- "C2" stopifnot( NodeProbs(C2)==c(0,1) ) C3[[]] <- 3 stopifnot( C3[[]] == "C3" ) ## Setting value of continuous node Cont[[]] <- 145.4 stopifnot( abs(Cont[[]] - 145.4) < .0001) ## Setting value with probabilities C2[] <- c(.3,.7) stopifnot( sum(abs(NodeProbs(C2)-c(.3,.7))) < .0001) C3[] <- c(1,2,1)/4 stopifnot( sum(abs(NodeProbs(C3)-c(.25,.5,.25))) < .0001) ## Automatic normalization C2[] <- .25 stopifnot( abs(sum(NodeProbs(C2)-c(.25,.75))) < .0001) C3[] <- c(1,1)/3 stopifnot( abs(sum(NodeProbs(C3)-1/3)) < .0001) ### Now some one parent cases AddLink(A,B) AddLink(A,B2) stopifnot( nrow(B[])==NodeNumStates(A), ncol(B[])==1+NodeNumStates(B), all(is.na(B[][,2:(1+NodeNumStates(B))])) ) NodeProbs(B) <- normalize(matrix(1:12,4)) Brow1 <- B[1] stopifnot( nrow(Brow1)==1,ncol(Brow1)==4, sum(abs(Brow1[,2:4]-c(1,5,9)/15))<.00001 ) Brow12 <- B[1:2] stopifnot( nrow(Brow12)==2,ncol(Brow12)==4, sum(abs(Brow12[2,2:4]-c(2,6,10)/18))<.00001 ) Brow4 <- B["A4"] stopifnot( nrow(Brow4)==1,ncol(Brow4)==4, sum(abs(Brow4[,2:4]-c(1,2,3)/6))<.00001 ) Brow34 <- B[c("A3","A4")] stopifnot( nrow(Brow34)==2,ncol(Brow34)==4, abs(sum(Brow4[1,2:4]-c(3,7,11)/21))<.00001 ) Ball <- B["*"] stopifnot( nrow(Ball)==4,ncol(Ball)==4 ) Ball <- B[EVERY_STATE] stopifnot( nrow(Ball)==4,ncol(Ball)==4 ) Brow24 <- B[data.frame(A=factor(c("A2","A4"),NodeStates(A)))] stopifnot( nrow(Brow24)==2,ncol(Brow24)==4, sum(abs(Brow24[2,2:4]-c(1,2,3)/6))<.00001 ) ## Set all rows to the same value. B[] <- matrix(c(1,1,1)/3,1) stopifnot( abs(NodeProbs(B)-1/3)<.0001 ) B[EVERY_STATE] <- matrix(c(1,2,1)/4,1) stopifnot( abs(NodeProbs(B)[3,]-c(.25,.5,.25))<.0001 ) B["*"] <- matrix(c(1,2,3)/6,1) stopifnot( abs(NodeProbs(B)[2,]-c(1/6,1/3,.5))<.0001 ) ## Setting to exact values B2[[1:2]] <- "B1" B2[[3]] <- "B2" B2[[4]] <- "B2" B2tab <- B2[[]] stopifnot( IsNodeDeterministic(B2), nrow(B2tab)==4,ncol(B2tab)==2, B2tab[,2] == c("B1","B1","B2","B2"), as.integer(B2tab[,2]) == c(1,1,2,2) ) ## Setting one value to non-deterministic changes the way the table is ## displayed. B2[2] <- c(.5,.5) B2tab <- B2[] stopifnot( !IsNodeDeterministic(B2), nrow(B2tab)==4,ncol(B2tab)==3, sum(abs(B2tab[2,2:3]- c(.5,.5))) < .001, B2tab[1,2:3] == c(1,0), B2[3,drop=TRUE] == c(0,1) ) ## Self-normalizing setting ## Not run: ## This will generate an error because it is trying to set all four ## configurations to the same value but it is given four values. B2[] <- c(.1,.2,.3,.4) ## End(Not run) B2[1:4] <- c(.1,.2,.3,.4) stopifnot( sum(abs(NodeProbs(B2)[,2]-c(.9,.8,.7,.6))) < .001 ) B2[1:2] <- .5 ## Set both values to the same thing B2[3:4] <- c(.6,.7) ## set to normalizing probs stopifnot( sum(abs(NodeProbs(B2)[,2]-c(.5,.5,.4,.3))) < .001 ) ## Beware! This next form assumes you are setting the rows to the same ## thing. B2[3:4] <- c(.2,.8) ## Ambiguous instructions stopifnot( sum(abs(NodeProbs(B2)[,2]-c(.5,.5,.8,.8))) < .001 ) ## Using a matrix makes intent clear B2[3:4] <- matrix(c(.2,.8),2) ## set to normalizing probs stopifnot( sum(abs(NodeProbs(B2)[,2]-c(.5,.5,.8,.2))) < .001 ) ## Data frame as value ## First do a blank extraction to get general shape. B2frame <- B2[] ## Now manipulate it however B2frame[,2:3] <- 1:8 ## And set it back B2[] <- normalize(B2frame) stopifnot( sum(abs(NodeProbs(B2)[,1]-c(1/6,2/8,3/10,4/12))) <.001 ) B2frame1 <-B2frame[B2frame$A=="A3",] B2frame1[,2:3] <- c(4,6)/10 B2[] <- B2frame1 ## Only row 3 affected stopifnot( sum(abs(NodeProbs(B2)[,1]-c(1/6,2/8,4/10,4/12))) <.001 ) ## Continuous node with one discrete parent AddLink(A,Cont) ##Notice how old value is replicated stopifnot( nrow(Cont[[]]) ==4, ncol(Cont[[]]) == 2, abs(Cont[[]][,2]-145.4) <.0001, abs(Cont[[3,drop=TRUE]]-145.4) <.0001 ) AddLink(A,CC) stopifnot( nrow(CC[]) ==4, ncol(CC[]) == 2, is.na(CC[][,2]) ) Cont[[]] <- 7 stopifnot( abs(Cont[[,drop=TRUE]]-7) <.0001 ) Cont[[2]] <- 3.2 stopifnot( abs(Cont[[,drop=TRUE]]-c(7,3.2,7,7)) <.0001 ) Cont[[1:2]] <- 0 Cont[[3:4]] <- c(8,1) stopifnot( abs(Cont[[,drop=TRUE]]-c(0,0,8,1)) <.0001, abs(Cont[[3:4,drop=TRUE]]-c(8,1)) < .0001 ) ## Two parent case AddLink(A,C2) AddLink(B,C2) C2[] <- c(.5,.5) stopifnot( nrow(C2[])==12, ncol(C2[])==4, sum(abs(numericPart(C2[])-.5)) < .0001 ) AddLink(A,C4) AddLink(B,C4) stopifnot( nrow(C4[])==12, ncol(C4[])==6, all(is.na(C4[,drop=TRUE])) ) NodeProbs(C4) <- normalize(array(1:48,c(4,3,4))) ## Data Frame/matrix Selection dfsel <- data.frame(A=factor(c("A2","A3"),levels=NodeStates(A)), B=factor(c("B1","B3"),levels=NodeStates(B))) C21.33 <- C4[dfsel] stopifnot( nrow(C21.33)==2, ncol(C21.33)==6, C21.33[1,1] == "A2", C21.33[2,2] == "B3", abs(C21.33[1,3]-2/80) < .0001, abs(C21.33[2,4]-23/116) < .0001 ) dfselbak <- data.frame(B=factor(c("B3","B2"),levels=NodeStates(B)), A=factor(c("A1","A4"),levels=NodeStates(A))) C13.42 <- C4[dfselbak] stopifnot( nrow(C13.42)==2, ncol(C13.42)==6, C13.42[1,1] == "A1", C13.42[2,2] == "B2", abs(C13.42[1,3]-9/108) < .0001, abs(C13.42[2,4]-20/104) < .0001 ) C2[dfsel] <- matrix(c(.7,.6,.3,.4),2) C2[dfselbak] <- c(.9,.1) stopifnot( sum(abs(numericPart(C2[])[,1] - c(.5,.7,.5,.5, .5,.5,.5,.9, .9,.5,.6,.5))) < .0001 ) ## Test for error with using variables in selection inside of a ## function. testSel <- function(node,sel1,sel2, val) { localselvar <- data.frame(sel1,sel2) names(localselvar) <- ParentNames(node) node[localselvar] node[localselvar]<-val invisible(node) } testSel(C2,factor(c("A2","A3"),levels=NodeStates(A)), factor(c("B1","B3"),levels=NodeStates(B)), matrix(c(.7,.6,.3,.4),2)) ## Array-like selection stopifnot( sum(abs(numericPart(C4[2,3])-c(10,22,34,46)/112))<.0001, sum(abs(numericPart(C4[B=2,A=4])-c(8,20,32,44)/104))<.0001 ) C1.23 <- C4[1,2:3] stopifnot( nrow(C1.23)==2, ncol(C1.23)==6, sum(abs(C1.23[,3] - c(5/92 ,9/108))) <.0001 ) C2[] <- .5 C2[1,2:3] <- .99 stopifnot( sum(abs(numericPart(C2[])[,1] - c(.5,.5,.5,.5, .99,.5,.5,.5, .99,.5,.5,.5))) < .0001 ) C1.23 <- C4["A1",c("B2","B3")] stopifnot( nrow(C1.23)==2, ncol(C1.23)==6, sum(abs(C1.23[,3] - c(5/92 ,9/108))) <.0001 ) C2[] <- .5 C2["A1",c("B2","B3")] <- .99 stopifnot( sum(abs(C2[,drop=TRUE][,1] - c(.5,.5,.5,.5, .99,.5,.5,.5, .99,.5,.5,.5))) < .0001 ) C34.12 <- C4[3:4,1:2] stopifnot( nrow(C34.12)==4, ncol(C34.12)==6, sum(abs(C34.12[,3] - c(3/84,4/88, 7/100, 8/104))) <.0001 ) C2[] <- .5 C2[3:4,1:2] <- .99 stopifnot( sum(abs(C2[][,3] - c(.5,.5,.99,.99, .5,.5,.99,.99, .5,.5,.5,.5))) < .0001 ) ## Wildcards C1. <- C4[1,EVERY_STATE] stopifnot( nrow(C1.) == 3, ncol(C1.)==6, sum(abs(C1.[,3] -c(1/76, 5/92, 9/108))) < .0001 ) C2[] <-.5 C2[1,EVERY_STATE] <- "C1" stopifnot( sum(abs(C2[][,3] - c(1,.5,.5,.5, 1,.5,.5,.5, 1,.5,.5,.5))) < .0001 ) C.2 <- C4[EVERY_STATE,2] stopifnot( nrow(C.2) == 4, ncol(C.2)==6, sum(abs(C.2[,3] -c(5/92, 6/96, 7/100, 8/104))) < .0001 ) C2[] <-.5 C2[[EVERY_STATE,2]] <- "C2" stopifnot( sum(abs(C2[drop=TRUE][,1] - c(.5,.5,.5,.5, 0,0,0,0, .5,.5,.5,.5))) < .0001 ) C1. <- C4["A1","*"] stopifnot( nrow(C1.) == 3, ncol(C1.)==6, sum(abs(C1.[,3] -c(1/76, 5/92, 9/108))) < .0001 ) C2[] <-.5 C2[["A1","*"]] <- "C1" stopifnot( sum(abs(C2[drop=TRUE][,1] - c(1,.5,.5,.5, 1,.5,.5,.5, 1,.5,.5,.5))) < .0001 ) C.2 <- C4["*","B2"] stopifnot( nrow(C.2) == 4, ncol(C.2)==6, sum(abs(C.2[,3] -c(5/92, 6/96, 7/100, 8/104))) < .0001 ) C2[] <-.5 C2[["*","B2"]] <- "C2" stopifnot( sum(abs(C2[drop=TRUE][,1] - c(.5,.5,.5,.5, 0,0,0,0, .5,.5,.5,.5))) < .0001 ) ## Missing parent values C1. <- C4[1,] stopifnot( nrow(C1.) == 3, ncol(C1.)==6, sum(abs(C1.[,3] -c(1/76, 5/92, 9/108))) < .0001 ) C2[] <-.5 C2[[1,]] <- "C1" stopifnot( sum(abs(C2[drop=TRUE][,1] - c(1,.5,.5,.5, 1,.5,.5,.5, 1,.5,.5,.5))) < .0001 ) C.2 <- C4[,2] stopifnot( nrow(C.2) == 4, ncol(C.2)==6, sum(abs(C.2[,3] -c(5/92, 6/96, 7/100, 8/104))) < .0001 ) C2[] <-.5 C2[[,2]] <- "C2" stopifnot( sum(abs(C2[drop=TRUE][,1] - c(.5,.5,.5,.5, 0,0,0,0, .5,.5,.5,.5))) < .0001 ) C1. <- C4[A=1] stopifnot( nrow(C1.) == 3, ncol(C1.)==6, sum(abs(C1.[,3] -c(1/76, 5/92, 9/108))) < .0001 ) C2[] <-.5 C2[[A=1]] <- "C1" stopifnot( sum(abs(C2[drop=TRUE][,1] - c(1,.5,.5,.5, 1,.5,.5,.5, 1,.5,.5,.5))) < .0001 ) C.2 <- C4[B="B2"] stopifnot( nrow(C.2) == 4, ncol(C.2)==6, sum(abs(C.2[,3] -c(5/92, 6/96, 7/100, 8/104))) < .0001 ) C2[] <-.5 C2[[B="B2"]] <- "C2" stopifnot( sum(abs(C2[drop=TRUE][,1] - c(.5,.5,.5,.5, 0,0,0,0, .5,.5,.5,.5))) < .0001 ) ## Data frame as value dfset <- data.frame(A=factor(c("A2","A3"),levels=NodeStates(A)), B=factor(c("B1","B3"),levels=NodeStates(B)), C.C1=c(1,0), C.C2=c(0,1)) C2[] <- .5 C2[] <- dfset stopifnot( sum(abs(C2[][,3] - c(.5,1,.5,.5, .5,.5,.5,.5, .5,.5,0,.5))) < .0001 ) ## Continuous Child node AddLink(B2,Cont) stopifnot( nrow(Cont[[]])==8, ncol(Cont[[]])==3, sum(abs(Cont[[drop=TRUE]]-c(0,0,8,1))) < .0001 ) AddLink(A,CCC) AddLink(B,CCC) stopifnot( nrow(CCC[])==12, ncol(CCC[])==3, all(is.na(CCC[][,3])) ) Cont[[]] <- 0 Cont[[1,1]] <- 1.1 Cont[[2:3,2]] <- c(2.2,3.2) Cont[["A4","*"]] <- 4 ## Not run: ## Can't set to multiple values when using * selection. Cont[["A4","*"]] <- c(4.1,4.2) ## Generates an error ## End(Not run) stopifnot( sum(abs(Cont[[drop=TRUE]]-c(1.1,0,0,4,0,2.2,3.2,4))) < .0001, abs(Cont[["A1","B1",drop=TRUE]]-1.1) <.0001, sum(abs(Cont[[B=2,A=2:3,drop=TRUE]]-c(2.2,3.2))) < .0001, sum(abs(Cont[[A=4,drop=TRUE]] -4)) < .0001 ) ## Set by integer count ## 12 rows in A*B combinations for (i in 1:12) { CCC[[i]] <- i C2[i] <- i/100 } stopifnot( sum(abs(CCC[[drop=TRUE]]-t(matrix(1:12,3,4)))) <.0001, sum(abs(C2[drop=TRUE][,1]-t(matrix(1:12/100,3,4)))) <.001 ) for (i in 1:12) { stopifnot( abs(CCC[[i,drop=TRUE]] - i) <.0001, abs(C2[i,drop=TRUE][1] - i/100) <.0001 ) } ### Try some things with three parents, just to make sure that works ### too. C2tab <- C2[drop=TRUE] AddLink(C3,C2) C2.1tab <- C2[,,"C1",drop=TRUE] stopifnot(all.equal(C2tab,C2.1tab), all.equal(C2tab,C2[,,"C1",drop=TRUE]), all.equal(C2tab,C2[C="C3",drop=TRUE])) stopifnot(all(abs(C2["A1","B1","C1",drop=TRUE]-NodeProbs(C2)[1,1,1,])<.0001), all.equal(C2["A1",,],C2[A="A1"]), all.equal(C2[,"B2",],C2[B="B2"]), all.equal(C2["A1","B2",],C2[B="B2",A="A1"])) DeleteNetwork(xnet) stopSession(sess)
This function fades a Netica conditional probability table associated with a node (that is, it makes it closer to uniform). This is used when learning conditional probabilities over time, so that newer observations will have more weight than older ones.
FadeCPT(node, degree = 0.2)
FadeCPT(node, degree = 0.2)
node |
A |
degree |
A scalar value between 0 and 1 providing the amount of fading to be done. A degree of 1 produces a uniform distribution and a degree of 0 leaves the CPT unchanged. |
This is essentially an exponential filter, with 1-degree
as the
retained weight. Calling it once with degree of and again
with degree
is equivalent to calling it once with degree
.
If prob
are the current probabilities associated with a row of
the CPT, and expr
is the current experience, then the new
probabilities will be newprob = normalize(prob* exper *
(1-degree) + degree)
, and the new experience will be the
normalization constant.
This function is often used together with LearnFindings
to down weight old cases when the conditional probabilities are thought
to be changing slowly over time.
This function returns the node object.
Frequently the degree is made time dependent. If dt
is the
time elapsed since the last observation, the degree is frequently an
expression like 1-expt(R,dt)
, where R
is a constant less
than 1 which controls how quickly the CPT is faded.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: FadeCPTable_bn()
NodeExperience
, NodeProbs
,
LearnFindings
sess <- NeticaSession() startSession(sess) aaa <- CreateNetwork("AAA", session=sess) A <- NewDiscreteNode(aaa,paste("A",1:5,sep=""),c("true","false")) for( i in 1:length(A)) { NodeProbs(A[[i]]) <- c(.8,.2) NodeExperience(A[[i]]) <- 10 } deg <- .2 expected <- NodeProbs(A[[1]])*10*(1-deg)+deg FadeCPT(A[[1]], deg) stopifnot( sum(abs(NodeProbs(A[[1]])-expected/sum(expected))) < .0001, abs(NodeExperience(A[[1]])-sum(expected)) < .001 ) ## Fading by deg then by deg2 is the same as fading by ## 1-(1-deg)*(1-deg2) deg2 <- .3 FadeCPT(A[[1]],deg2) FadeCPT(A[[2]], 1-(1-deg)*(1-deg2)) stopifnot ( sum(abs(NodeProbs(A[[1]]) - NodeProbs(A[[2]]))) < .0001 ) ## Fade by two time units. lambda <- .8 FadeCPT(A[[3]],1-lambda^2) ## Special cases FadeCPT(A[[4]],0) FadeCPT(A[[5]],1) stopifnot ( sum(abs(NodeProbs(A[[4]]) -c(.8,.2))) < .0001, sum(abs(NodeProbs(A[[5]]) -c(.5,.5))) < .0001 ) DeleteNetwork(aaa) stopSession(sess)
sess <- NeticaSession() startSession(sess) aaa <- CreateNetwork("AAA", session=sess) A <- NewDiscreteNode(aaa,paste("A",1:5,sep=""),c("true","false")) for( i in 1:length(A)) { NodeProbs(A[[i]]) <- c(.8,.2) NodeExperience(A[[i]]) <- 10 } deg <- .2 expected <- NodeProbs(A[[1]])*10*(1-deg)+deg FadeCPT(A[[1]], deg) stopifnot( sum(abs(NodeProbs(A[[1]])-expected/sum(expected))) < .0001, abs(NodeExperience(A[[1]])-sum(expected)) < .001 ) ## Fading by deg then by deg2 is the same as fading by ## 1-(1-deg)*(1-deg2) deg2 <- .3 FadeCPT(A[[1]],deg2) FadeCPT(A[[2]], 1-(1-deg)*(1-deg2)) stopifnot ( sum(abs(NodeProbs(A[[1]]) - NodeProbs(A[[2]]))) < .0001 ) ## Fade by two time units. lambda <- .8 FadeCPT(A[[3]],1-lambda^2) ## Special cases FadeCPT(A[[4]],0) FadeCPT(A[[5]],1) stopifnot ( sum(abs(NodeProbs(A[[4]]) -c(.8,.2))) < .0001, sum(abs(NodeProbs(A[[5]]) -c(.5,.5))) < .0001 ) DeleteNetwork(aaa) stopSession(sess)
"FileCaseStream"
This object is subclass of CaseStream
so it is a
wrapper around a Netica stream which is used to read/write cases. In
this subclass, the case stream is associated with a Netica case file
(‘.cas’ extension). The function CaseFileStream
is the constructor. The function ReadFindings
reads the
findings from the stream and the function WriteFindings
writes them out.
Class "CaseStream"
, directly.
All reference classes extend and inherit methods from
"envRefClass"
. Note that because this is a reference
class unlike traditional S3 and S4 classes it can be destructively
modified. Also fields (slots) are accessed using the ‘$’
operator.
Note these should be regarded as read-only from user code.
Name
:Object of class character
used in printed
representation. Default is
basename(Case_Stream_Path)
.
Session
:Object of class NeticaSession
a link
to the session in which this case stream was created.
Netica_Case_Stream
:Object of class externalptr
a pointer to the case stream in Netica memory.
Case_Stream_Position
:Object of class integer
the number of the last read/writen record. This is NA
if
the end of the file has been reached.
Case_Stream_Lastid
:Object of class integer
the
ID number of the last read/written record.
Case_Stream_Lastfreq
:Object of class numeric
giving the frequence of the last read/written record. This is
used as a weight in learning applications.
open()
:Opens a connection too the file in Netica.
show()
:Provides a description of the field
initialize(Name, Session, Case_Stream_Path, ...)
:internal constructor; user code should use CaseFileStream
.
The following methods are inherited (from CaseStream
):
close ("CaseStream"), isActive ("CaseStream"), isOpen ("CaseStream"),
show ("CaseStream"), clearErrors ("CaseStream"), reportErrors
("CaseStream"), signalErrors ("CaseStream"), initialize ("CaseStream")
In version 0.5 of RNetica, this class was renamed. It is now called
FileCaseStream
but the constructor is still called
CaseFileStream
(while previously the class and the
filename had the same name). This matches the usage of
FileCaseStream
and its constructor
CaseFileStream
. It is also now a reference class
instead of an informal S3 class. This is only likely to be a problem
for code that was using the hard coded class name.
Stream objects are fragile, and will not survive saving and restoring
an R session. However, the object retains information about itself,
so that calling OpenCaseStream
on the saved object, should
reopen the stream. Note that any position information will be lost.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: NewFileStream_ns(), DeleteStream_ns() http://homepage.stat.uiowa.edu/~luke/R/references/weakfinex.html
See CaseStream
for the superclass and
MemoryCaseStream
for a sibling class.
The function CaseFileStream
is the constructor.
OpenCaseStream
,
CaseFileDelimiter
, CaseFileMissingCode
,
WriteFindings
, ReadFindings
,
WithOpenCaseStream
sess <- NeticaSession() startSession(sess) abc <- CreateNetwork("ABC", session=sess) A <- NewDiscreteNode(abc,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc,"C",c("C1","C2")) AddLink(A,B) AddLink(A,C) AddLink(B,C) ## Outputfilename casefile <- tempfile("testcase",fileext=".cas") filestream <- CaseFileStream(casefile, session=sess) stopifnot(is.CaseFileStream(filestream), isCaseStreamOpen(filestream)) ## Case 1 NodeFinding(A) <- "A1" NodeFinding(B) <- "B1" NodeFinding(C) <- "C1" filestream <- WriteFindings(list(A,B,C),filestream,1001,1.0) stopifnot(getCaseStreamLastId(filestream)==1001, abs(getCaseStreamLastFreq(filestream)-1.0) <.0001) ## Close it filestream <- CloseCaseStream(filestream) stopifnot (is.CaseFileStream(filestream), !isCaseStreamOpen(filestream)) ## Reopen it filestream <- OpenCaseStream(filestream) stopifnot (is.CaseFileStream(filestream), isCaseStreamOpen(filestream)) ##Case 1 RetractNetFindings(abc) filestream <- ReadFindings(list(A,B,C),filestream,"FIRST") stopifnot(getCaseStreamLastId(filestream)==1001, abs(getCaseStreamLastFreq(filestream)-1.0) <.0001) ##Clean Up CloseCaseStream(filestream) DeleteNetwork(abc) stopSession(sess)
sess <- NeticaSession() startSession(sess) abc <- CreateNetwork("ABC", session=sess) A <- NewDiscreteNode(abc,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc,"C",c("C1","C2")) AddLink(A,B) AddLink(A,C) AddLink(B,C) ## Outputfilename casefile <- tempfile("testcase",fileext=".cas") filestream <- CaseFileStream(casefile, session=sess) stopifnot(is.CaseFileStream(filestream), isCaseStreamOpen(filestream)) ## Case 1 NodeFinding(A) <- "A1" NodeFinding(B) <- "B1" NodeFinding(C) <- "C1" filestream <- WriteFindings(list(A,B,C),filestream,1001,1.0) stopifnot(getCaseStreamLastId(filestream)==1001, abs(getCaseStreamLastFreq(filestream)-1.0) <.0001) ## Close it filestream <- CloseCaseStream(filestream) stopifnot (is.CaseFileStream(filestream), !isCaseStreamOpen(filestream)) ## Reopen it filestream <- OpenCaseStream(filestream) stopifnot (is.CaseFileStream(filestream), isCaseStreamOpen(filestream)) ##Case 1 RetractNetFindings(abc) filestream <- ReadFindings(list(A,B,C),filestream,"FIRST") stopifnot(getCaseStreamLastId(filestream)==1001, abs(getCaseStreamLastFreq(filestream)-1.0) <.0001) ##Clean Up CloseCaseStream(filestream) DeleteNetwork(abc) stopSession(sess)
This function assumes that the network has been compiled and that a number of findings have been entered. The function calculates the prior probability for the entered findings (that is, the normalization constant of the Bayesian network).
FindingsProbability(net)
FindingsProbability(net)
net |
An active and compiled Bayesian Network (class
|
In the usual algorithms for propagating probabilities in a Bayesian
network the probabilities are passed unnormalized. When reporting the
probabilities, a normalization constant is calculated. This
normalization constant is the probability of all of the findings that
have been entered through NodeFinding()
. (See Almond,
1995, for details on the use of normalization constants as
probabilities of findings.)
It is not meaningful to call this function before the network has been
compiled. Calling it before findings have been entered will result in
a value of 1.0
.
A scalar real value representing the probability of the findings, or
NA
if the network was not found or not compiled.
Netica gives a warning about the interpretation if likelihood findings
have been set (through NodeLikelihood()
. In this case,
the value is perhaps better though of as a normalization constant.
Russell Almond
Almond, R. G. (1995) Graphical Belief Modeling. Chapman and Hall.
http://norsys.com/onLineAPIManual/index.html: FindingsProbability_bn()
NeticaNode
,NeticaBN
,
NodeBeliefs()
,
EnterNegativeFinding()
,
RetractNodeFinding()
, NodeLikelihood()
sess <- NeticaSession() startSession(sess) EMSMMotif <- ReadNetworks(system.file("sampleNets","EMSMMotif.dne", package="RNetica"), session=sess) CompileNetwork(EMSMMotif) norm1 <- FindingsProbability(EMSMMotif) stopifnot ( abs(norm1-1) <.0001) ## Find observable nodes obs <- NetworkNodesInSet(EMSMMotif,"Observable") NodeFinding(obs$Obs1a1) <- "Right" NodeFinding(obs$Obs1a2) <- "Wrong" prob1r2w <- FindingsProbability(EMSMMotif) stopifnot (prob1r2w < 1, prob1r2w > 0) ## Clear it out and try again RetractNetFindings(EMSMMotif) NodeLikelihood(obs$Obs2a) <- c(.75,.75,.75) prob75 <- FindingsProbability(EMSMMotif) stopifnot( abs(prob75-.75) < .0001) DeleteNetwork(EMSMMotif) stopSession(sess)
sess <- NeticaSession() startSession(sess) EMSMMotif <- ReadNetworks(system.file("sampleNets","EMSMMotif.dne", package="RNetica"), session=sess) CompileNetwork(EMSMMotif) norm1 <- FindingsProbability(EMSMMotif) stopifnot ( abs(norm1-1) <.0001) ## Find observable nodes obs <- NetworkNodesInSet(EMSMMotif,"Observable") NodeFinding(obs$Obs1a1) <- "Right" NodeFinding(obs$Obs1a2) <- "Wrong" prob1r2w <- FindingsProbability(EMSMMotif) stopifnot (prob1r2w < 1, prob1r2w > 0) ## Clear it out and try again RetractNetFindings(EMSMMotif) NodeLikelihood(obs$Obs2a) <- c(.75,.75,.75) prob75 <- FindingsProbability(EMSMMotif) stopifnot( abs(prob75-.75) < .0001) DeleteNetwork(EMSMMotif) stopSession(sess)
This function generates a random instantiation of the nodes in
nodelist
using the current (that is posterior to any findings
entered into the net) joint probability distribution of those nodes in
the network.
GenerateRandomCase(nodelist, method = "Default", timeout = 100, rng = NULL)
GenerateRandomCase(nodelist, method = "Default", timeout = 100, rng = NULL)
nodelist |
A list of active |
method |
A character scalar used to describe the method used
select the random numbers. This should have one of the values
|
timeout |
This is a number describing how long to carry on computations under the forward sampling method. It is ignored under the join tree sampling method or when the default sampling method turns out to be join tree. |
rng |
This either be an existing |
The function visits each node in nodelist
and randomly sets a
finding for that node based on the current beliefs about that node.
This takes into account any findings previously entered into the
graph (including the previously sampled nodes in the list). In
particular, to generate multiple cases, the findings need to be
retracted (using RetractNodeFinding(node)
or
RetractNetFindings(net)
between each generation.
Netica supports three methods for doing the sampling:
For each node in turn, the beliefs are calculated and a random state is selected and entered as a finding (with beliefs propagating). The network must be compiled for this method to work.
Random cases are generated directly using
equations for continuous nodes if these are available. Random results
not compatible with the current findings are rejected. This method is
not guaranteed to converge, and may be quite slow if the current set
of findings has a low probability. It will only run for a period of
time indicated by timeout
and returns a negative value if it
does not complete successfully.
Netica figures out which method is better to use. It uses forward sampling if either rejections aren't a problem (presumably because there are no findings) or if the network is uncompiled. Otherwise it uses join tree sampling.
The rng
argument can be used to associate a random number
generator with the generation (see NeticaRNG
). If the
rng
argument is NULL
, then the default random number
generator for the network is used. This is either a random number
generator associated with the network using
NetworkSetRNG
, or else the default Netica random number
generator.
Invisibly returns 0 if the case was successfully generated or -1 if the case could not be generated (using the forward sampling method). In the latter case, a warning is issued as well.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GenerateRandomCase_bn()
NetworkSetRNG()
, NeticaRNG()
,
NodeFinding
, RetractNetFindings
ReadFindings
, CaseStream
sess <- NeticaSession() startSession(sess) irt5 <- ReadNetworks(system.file("sampleNets","IRT5.dne", package="RNetica"), session=sess) irt5.theta <- NetworkFindNode(irt5,"Theta") irt5.x <- NetworkFindNode(irt5,paste("Item",1:5,sep="_")) CompileNetwork(irt5) GenerateRandomCase(irt5.x) sapply(irt5.x,NodeFinding) RetractNetFindings(irt5) GenerateRandomCase(irt5.x) sapply(irt5.x,NodeFinding) ## This generates a fixed series of random cases and saves them to a ## file. N <- 10L rnodes <- c(list(irt5.theta),irt5.x) casefile <- tempfile("irt5testcase",fileext=".cas") filestream <- CaseFileStream(casefile, session=sess) rng <- NewNeticaRNG(123456779, session=sess) WithOpenCaseStream(filestream, WithRNG(rng, for (n in 1L:N) { GenerateRandomCase(rnodes,rng=rng) WriteFindings(rnodes,filestream,n) lapply(rnodes,RetractNodeFinding) # Only retract findings for # generated nodes })) ## With constructs force closure even on error exit. stopifnot(!isNeticaRNGActive(rng), !isCaseStreamOpen(filestream)) DeleteNetwork(irt5) stopSession(sess)
sess <- NeticaSession() startSession(sess) irt5 <- ReadNetworks(system.file("sampleNets","IRT5.dne", package="RNetica"), session=sess) irt5.theta <- NetworkFindNode(irt5,"Theta") irt5.x <- NetworkFindNode(irt5,paste("Item",1:5,sep="_")) CompileNetwork(irt5) GenerateRandomCase(irt5.x) sapply(irt5.x,NodeFinding) RetractNetFindings(irt5) GenerateRandomCase(irt5.x) sapply(irt5.x,NodeFinding) ## This generates a fixed series of random cases and saves them to a ## file. N <- 10L rnodes <- c(list(irt5.theta),irt5.x) casefile <- tempfile("irt5testcase",fileext=".cas") filestream <- CaseFileStream(casefile, session=sess) rng <- NewNeticaRNG(123456779, session=sess) WithOpenCaseStream(filestream, WithRNG(rng, for (n in 1L:N) { GenerateRandomCase(rnodes,rng=rng) WriteFindings(rnodes,filestream,n) lapply(rnodes,RetractNodeFinding) # Only retract findings for # generated nodes })) ## With constructs force closure even on error exit. stopifnot(!isNeticaRNGActive(rng), !isCaseStreamOpen(filestream)) DeleteNetwork(irt5) stopSession(sess)
This searches through the currently open Netica networks in the
NeticaSession
and returns a
NeticaBN
object pointing to the networks with the given
names. If no network with the name is found NULL
is returned
instead, so this provides a way to check whether a network exists.
CheckNamedNetworks
checks the internal Netica list of networks,
not the networks cached in the NeticaSession
object, so it can be used to check for inconsistencies.
GetNamedNetworks(namelist, session=getDefaultSession()) CheckNamedNetworks(namelist, session=getDefaultSession())
GetNamedNetworks(namelist, session=getDefaultSession()) CheckNamedNetworks(namelist, session=getDefaultSession())
namelist |
A character vector giving the name or names of the networks to be found. |
session |
An object of type |
GetNamedNetworks()
searches the list of network names looking for
a network with the appropriate name. If it is found, a handle to that
network is returned as a NeticaBN
object. If
not, NULL
is returned. Note that if a network of the specified
name existed, it could return an inactive
NeticaBN
object corresponding to the deleted
network, so it is probably good to check the result with
is.active
.
There are two ways that RNetica can check for a network of a given
name. The first the network cache maintained by the
NeticaSession
object (session$nets
). The
function GetNamedNetworks
just checks the cache, so it should
be relatively fast. The function CheckNamedNetworks
iterates
through all of the networks in Netica's internal memory, so it should
be slower, but should also spot problems with RNetica and Netica
getting out of sync.
If namelist
is of length 1, then a single
NeticaBN
object or NULL
will be returned.
If namelist
is of length greater than 1, then a list of the
same length as namelist
is returned. Each element is a
NeticaBN
related to the corresponding name or NULL
if
the name does not refer to a network.
Each NeticaBN
is given a name when it is
created. When the network is created, either through a call to
CreateNetwork
or ReadNetworks
, the
NeticaSession
object updates its cache of the network
names in its nets
field. The nets
field of the session
object is an environment
which associate the
network's name with a NeticaBN
object.
Internally, functions that return a NeticaBN
object (primarily NodeNet
), search the network cache in
the session object for the network with the corresponding name.
GetNamedNetworks
uses the cache (which is hashed) and so should
be fairly fast.
CheckNamedNetworks
does a linear search through all networks,
so it could be pretty slow if there are a large number of networks
open. It should raise an error if the cache and the internal Netica
specs are out of sync.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNthNet_bn()
CreateNetwork()
, GetNthNetwork()
sess <- NeticaSession() startSession(sess) net1 <- CreateNetwork("myNet", session=sess) ## Fetch the network we just created by name. net2 <- GetNamedNetworks("myNet", session=sess) stopifnot(is(net2,"NeticaBN")) stopifnot(NetworkName(net1)==NetworkName(net2)) stopifnot(net1==net2) net3 <- CheckNamedNetworks("myNet", session=sess) stopifnot(net1==net3) ## No network named "fish", this should return NULL fish <- GetNamedNetworks("fish", session=sess) stopifnot(all(sapply(fish,is.null))) fish <- CheckNamedNetworks("fish", session=sess) stopifnot(all(sapply(fish,is.null))) DeleteNetwork(net1) net1a <- GetNamedNetworks("myNet", session=sess) stopifnot(NetworkName(net1a)=="myNet",!is.active(net1a)) net1b <- CheckNamedNetworks("myNet", session=sess) stopifnot(is.null(net1b)) stopSession(sess)
sess <- NeticaSession() startSession(sess) net1 <- CreateNetwork("myNet", session=sess) ## Fetch the network we just created by name. net2 <- GetNamedNetworks("myNet", session=sess) stopifnot(is(net2,"NeticaBN")) stopifnot(NetworkName(net1)==NetworkName(net2)) stopifnot(net1==net2) net3 <- CheckNamedNetworks("myNet", session=sess) stopifnot(net1==net3) ## No network named "fish", this should return NULL fish <- GetNamedNetworks("fish", session=sess) stopifnot(all(sapply(fish,is.null))) fish <- CheckNamedNetworks("fish", session=sess) stopifnot(all(sapply(fish,is.null))) DeleteNetwork(net1) net1a <- GetNamedNetworks("myNet", session=sess) stopifnot(NetworkName(net1a)=="myNet",!is.active(net1a)) net1b <- CheckNamedNetworks("myNet", session=sess) stopifnot(is.null(net1b)) stopSession(sess)
Netica networks can either propagate the effects of new findings immediately, or they can delay propagation until the user queries the network. These functions toggle the switch that controls the autoupdate mechanism
GetNetworkAutoUpdate(net) SetNetworkAutoUpdate(net, newautoupdate) WithoutAutoUpdate(net,expr)
GetNetworkAutoUpdate(net) SetNetworkAutoUpdate(net, newautoupdate) WithoutAutoUpdate(net,expr)
net |
A |
newautoupdate |
A logical values, |
expr |
An R expression to be evaluated with automatic updating turned off. |
Automatic updating means that queries operate very quickly, however, if a large number of finding are to be entered before the next query, they can slow the network down. These functions provide a mechanism for controlling that.
GetNetworkAutoUpdate()
returns the current status of the
autoupdate flag. SetNetworkAutoUpdate()
sets flag, but
returns its current value (to make it easier to restore). The
function WithoutAutoUpdate
provides a mechanism for turning
updating off while performing a series of operations.
GetNetworkAutoUpdate()
and SetNetworkAutoUpdate
both
returns the current autoupdate flag as a logical value.
WithoutAutoUpdate()
returns the value of executing expr
,
unless executing expr
results in an error in which case it
returns a try-error
.
Automatic updating makes a lot of sense when Netica is running under the GUI, but not so much when it is running as an API. It is probably easiest to just set this to false all the time.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: SetNetAutoUpdate_bn(), GetNetAutoUpdate_bn()
NeticaBN
, NodeBeliefs()
,
NodeFinding()
sess <- NeticaSession() startSession(sess) autoNet <- CreateNetwork("AutomaticTest", session=sess) GetNetworkAutoUpdate(autoNet) SetNetworkAutoUpdate(autoNet,FALSE) stopifnot(!GetNetworkAutoUpdate(autoNet)) stopifnot(!SetNetworkAutoUpdate(autoNet,TRUE)) stopifnot(GetNetworkAutoUpdate(autoNet)) result <- TRUE WithoutAutoUpdate(autoNet, result <<-GetNetworkAutoUpdate(autoNet)) stopifnot(!result) DeleteNetwork(autoNet) stopSession(sess)
sess <- NeticaSession() startSession(sess) autoNet <- CreateNetwork("AutomaticTest", session=sess) GetNetworkAutoUpdate(autoNet) SetNetworkAutoUpdate(autoNet,FALSE) stopifnot(!GetNetworkAutoUpdate(autoNet)) stopifnot(!SetNetworkAutoUpdate(autoNet,TRUE)) stopifnot(GetNetworkAutoUpdate(autoNet)) result <- TRUE WithoutAutoUpdate(autoNet, result <<-GetNetworkAutoUpdate(autoNet)) stopifnot(!result) DeleteNetwork(autoNet) stopSession(sess)
Fetches networks according to an internal sequence list of networks
maintained inside of Netica. If the number passed is greater than the
number of currently defined networks, this function will return NULL
GetNthNetwork(n, session = getDefaultSession())
GetNthNetwork(n, session = getDefaultSession())
n |
A vector of integers greater than 1. |
session |
An object of class |
The primary use for this function is probably to loop through all open
networks. As this function will return NULL
when there are no
more networks, that can be used to terminate the loop.
Note that the sequence numbers can change, particularly after functions that open and close networks.
This is a wrapper for the Netica function GetNthNet_bn()
.
Starting with RNetica 0.5, the session object is a container which contains the open networks, so this function is no longer really needed.
If n
is of length 1, then a single NeticaBN
object or NULL
will be returned.
If n
is of length greater than 1, then a list of the
same length as n
is returned. Each element is a
NeticaBN
related or NULL
if the number is greater than
the number of open networks.
The Netica shared library uses a zero-based reference (i.e., the first net is 0), but this function subtracts 1 from the argument, so it uses a one-based reference system (the first net is 1).
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNthNet_bn()
NeticaSession
,
CreateNetwork()
, GetNamedNetworks()
sess <- NeticaSession() startSession(sess) foo <- CreateNetwork("foo",sess) bar <- CreateNetwork("bar",sess) count <- 1 while (!is.null(net <- GetNthNetwork(count,sess))) { cat("Network number ",count," is ",NetworkName(net),".\n") count <- count +1 } cat("Found ",count-1," networks.\n") stopifnot(count==3L) stopSession(sess)
sess <- NeticaSession() startSession(sess) foo <- CreateNetwork("foo",sess) bar <- CreateNetwork("bar",sess) count <- 1 while (!is.null(net <- GetNthNetwork(count,sess))) { cat("Network number ",count," is ",NetworkName(net),".\n") count <- count +1 } cat("Found ",count-1," networks.\n") stopifnot(count==3L) stopSession(sess)
This function tests to see if a conditional probability table has been
assigned to node
. The function returns two values, the first
tests for existence of the table, the second tests for a complete
table (no NAs).
HasNodeTable(node)
HasNodeTable(node)
node |
An active |
This function returns two values. The first is true or false
according to whether the conditional probability table has been
established, that is has NodeProbs()
been set. The
second value tests to see whether the conditional probability table is
complete, that is, does it have any NA
s associated with it.
In many cases, it is the second value that is of interest, so
all(HasNodeTable(node))
is often a useful idiom.
A logical vector with two elements. The first states whether or not the node has any of its conditional probabilities set. The second tests whether or not the table has been completely specified.
Generating incomplete tables is pretty hard to do in RNetica, a row
must be deliberately set to NA
. However, a network read in
from a file might have incomplete tables.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: HasNodeTable_bn()
NeticaNode
, NodeParents()
,
NodeInputNames()
, DeleteNodeTable()
sess <- NeticaSession() startSession(sess) ab1 <- CreateNetwork("AB1", session=sess) A <- NewDiscreteNode(ab1,"A",c("A1","A2","A3")) B <- NewDiscreteNode(ab1,"B",c("B1","B2")) AddLink(A,B) ##Nodes start undefined. stopifnot( HasNodeTable(A)==c(FALSE,FALSE) ) NodeProbs(A) <- c(0,1,0) stopifnot( HasNodeTable(A)==c(TRUE,TRUE) ) for (node in NetworkAllNodes(ab1)) { if (!all(HasNodeTable(node))) { cat("Node ", toString(node), " still needs a conditional probability table.\n") } } DeleteNetwork(ab1) stopSession(sess)
sess <- NeticaSession() startSession(sess) ab1 <- CreateNetwork("AB1", session=sess) A <- NewDiscreteNode(ab1,"A",c("A1","A2","A3")) B <- NewDiscreteNode(ab1,"B",c("B1","B2")) AddLink(A,B) ##Nodes start undefined. stopifnot( HasNodeTable(A)==c(FALSE,FALSE) ) NodeProbs(A) <- c(0,1,0) stopifnot( HasNodeTable(A)==c(TRUE,TRUE) ) for (node in NetworkAllNodes(ab1)) { if (!all(HasNodeTable(node))) { cat("Node ", toString(node), " still needs a conditional probability table.\n") } } DeleteNetwork(ab1) stopSession(sess)
The function is.IDname()
returns a logical vector indicating
whether or not each element of x
is a valid Netica identifier.
The function as.IDname()
attempts to massage the input value to
conform to the IDname rules.
is.IDname(x) as.IDname(x,prefix="y",maxlen=25)
is.IDname(x) as.IDname(x,prefix="y",maxlen=25)
x |
A character vector of possible identifier names. |
prefix |
A character scalar that provides an alphabetic prefix for names that start with an illegal character. |
maxlen |
The maximum number of characters to use in the converted name, which should be less than Netica's maximum of 30 characters. |
Netica identifiers (net names, node names, state names, and similar)
are limited to 30 characters which must be a valid letter, number or
the character '_'. The first character must be a letter. The function
is.IDname()
tests to see if a string conforms to these rules,
and thus is a legal name.
The function as.IDname()
attempts to coerce its argument into
the IDname format by applying the following transformations.
The argument is coerced into a character vector.
If any value begins with a nonalphabetic character, the
prefix
argument is prepended to all values.
All non-alphanumeric characters are converted to '_'.
Each value is truncated to maxlen
characters in length.
The truncation works by the following mechanism:
The string is truncated to length maxlen-3
.
The UTF 8 values of the remaining characters is summed and the result is taken modulo 100 to provide a 2-digit hash code for the remaining characters.
The hash code is appended to the end of the truncated string
separated with an _
.
This should result in strings which are likely, but not guaranteed to
be unique if the difference between two names is only after the last
maxlen-3
characters.
Note that although Netica allows variable names up to 30 characters in
length, in some cases (particularly when stub variables are created
after separating an edge from its parent) Netica creates new variable
names by appending characters onto existing ones. That is why the
recommended value for maxlen
is set to 25.
A logical vector of the same length of x
.
This is primarily a utility for doing argument checking inside of functions that require a Netica IDname.
Russell Almond
http://norsys.com/onLineAPIManual/index.html
CreateNetwork()
, NewDiscreteNode()
,
NodeStates()
, NodeName()
,
NodeInputNames()
,
stopifnot( is.IDname(c("aFish","Wanda1","feed me","fish_food","1more","US$", "a123456789012345678901234567890")) == c(TRUE,TRUE,FALSE,TRUE,FALSE,FALSE,FALSE), as.IDname(c("aFish","Wanda1","feed me","fish_food","1more","US$", "a123456789012345678901234567890")) == c("aFish","Wanda1","feed_me","fish_food","y1more","US_", "a123456789012345678901_25") )
stopifnot( is.IDname(c("aFish","Wanda1","feed me","fish_food","1more","US$", "a123456789012345678901234567890")) == c(TRUE,TRUE,FALSE,TRUE,FALSE,FALSE,FALSE), as.IDname(c("aFish","Wanda1","feed me","fish_food","1more","US$", "a123456789012345678901234567890")) == c("aFish","Wanda1","feed_me","fish_food","y1more","US_", "a123456789012345678901_25") )
NeticaSession
, NeticaBN
,
NeticaNode
, CaseStream
, and
NeticaRNG
objects all contain embedded pointers
into Netica's memory. The function
is.active()
checks to see that the corresponding Netica object
still exists.
is.active(x)
is.active(x)
x |
A |
Internally, NeticaSession
,
NeticaBN
and
NeticaNode
objects all contain pointers to the
corresponding Netica objects. The DeleteNetwork()
and
DeleteNodes()
functions deletes the Netica objects (and
clears the pointers in the R objects). It is difficult to control
when R objects are deleted, especially if they are protected in data
structures that are saved in the workspace. The function
is.active()
is meant to check if the corresponding object is
still valid. In most cases, RNetica will give an error (or at least a
warning) if an inactive object is supplied as an argument.
For CaseStream
objects (and its sub-classes
FileCaseStream
and
MemoryCaseStream
) active and open have the same
meaning.
For NeticaRNG
objects, they become inactive when
they are freed.
Note that the function StopNetica()
should make all
NeticaBN
and NeticaNode
objects inactive. Thus, these
objects cannot be saved from one R session to another, and should be
recreated when needed. In particular, any Netica object restored from
a saved workspace should be inactive.
The function is.active()
returns TRUE
if the argument
still points to a network or node loaded in Netica's memory, and
FALSE
if that network or node has been deleted. It returns
NA
if the argument is not a NeticaSession
,
NeticaBN
, NeticaNode
, CaseStream
, or
NeticaRNG
object.
If x
is a list, then a logical vector of the same length of
x
is returned with is.active()
recursively applied to
each one.
The actual test done is to test the pointer to see if it is null or not. It should be the case that when an R object is disconnected from its Netica counterpart, the pointer is set to null.
Russell Almond
http://norsys.com/onLineAPIManual/index.html, http://lib.stat.cmu.edu/R/CRAN/doc/manuals/R-exts.html
StopNetica()
, NeticaBN
,
DeleteNetwork()
, NeticaNode
,
DeleteNodes()
, NeticaSession
,
CaseStream
, NeticaRNG
sess <- NeticaSession() stopifnot(!is.active(sess)) startSession(sess) stopifnot(is.active(sess)) anet <- CreateNetwork("ActiveNet", session=sess) stopifnot(is.active(anet)) anodes <- NewContinuousNode(anet,paste("ActiveNode",1:2,sep="")) stopifnot(all(is.active(anodes))) inode <- DeleteNodes(anodes[[1]]) stopifnot(!is.active(anodes[[1]])) stopifnot(!is.active(inode)) stopifnot(is.active(anodes[[2]])) DeleteNetwork(anet) stopifnot(!is.active(anet)) ## Node gets deleted along with network stopifnot(!any(is.active(anodes))) rng <- NewNeticaRNG(1, session=sess) stopifnot(is.active(rng)) FreeNeticaRNG(rng) stopifnot(!is.active(rng)) casefile <- tempfile("testcase",fileext=".cas") filestream <- CaseFileStream(casefile, session=sess) stopifnot(is.active(filestream)) CloseCaseStream(filestream) stopifnot(!is.active(filestream)) stopSession(sess) stopifnot(!is.active(sess))
sess <- NeticaSession() stopifnot(!is.active(sess)) startSession(sess) stopifnot(is.active(sess)) anet <- CreateNetwork("ActiveNet", session=sess) stopifnot(is.active(anet)) anodes <- NewContinuousNode(anet,paste("ActiveNode",1:2,sep="")) stopifnot(all(is.active(anodes))) inode <- DeleteNodes(anodes[[1]]) stopifnot(!is.active(anodes[[1]])) stopifnot(!is.active(inode)) stopifnot(is.active(anodes[[2]])) DeleteNetwork(anet) stopifnot(!is.active(anet)) ## Node gets deleted along with network stopifnot(!any(is.active(anodes))) rng <- NewNeticaRNG(1, session=sess) stopifnot(is.active(rng)) FreeNeticaRNG(rng) stopifnot(!is.active(rng)) casefile <- tempfile("testcase",fileext=".cas") filestream <- CaseFileStream(casefile, session=sess) stopifnot(is.active(filestream)) CloseCaseStream(filestream) stopifnot(!is.active(filestream)) stopSession(sess) stopifnot(!is.active(sess))
A NeticaNode
object can take on either a discrete set of
values or an arbitrary real value. These functions determine which
type of node this is.
is.discrete(node) is.continuous(node)
is.discrete(node) is.continuous(node)
node |
A |
While in the Netica GUI, one first creates a node and then determines
whether it will be discrete or continuous, in the API this is
determined at the time of creation (by calling
NewContinuousNode()
or NewDiscreteNode()
.
These functions determine which type of node the given node is.
Note that setting NodeLevels
can make a continuous node
behave like a discrete one and vice versa. For continuous nodes, the
levels are cut points for getting a discrete state from the node. For
a discrete node, the levels are real values representing the midpoint
of the states.
TRUE
or FALSE
depending on whether a node is discrete or
continuous.
Currently, this function does not actually look at the internal Netica
state, but rather looks at the field "discrete"
which
is set when the node is created.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNodeType_bn(), SetNodeLevels_bn()
NewDiscreteNode()
, NewContinuousNode()
,
NeticaNode
, NodeLevels()
,
NodeStates()
sess <- NeticaSession() startSession(sess) netx <- CreateNetwork("netx", session=sess) bnode <- NewDiscreteNode(netx,"bool",c("True","False")) stopifnot(is.discrete(bnode)) stopifnot(!is.continuous(bnode)) rnode <- NewContinuousNode(netx,"real") stopifnot(!is.discrete(rnode)) stopifnot(is.continuous(rnode)) DeleteNetwork(netx) stopSession(sess)
sess <- NeticaSession() startSession(sess) netx <- CreateNetwork("netx", session=sess) bnode <- NewDiscreteNode(netx,"bool",c("True","False")) stopifnot(is.discrete(bnode)) stopifnot(!is.continuous(bnode)) rnode <- NewContinuousNode(netx,"real") stopifnot(!is.discrete(rnode)) stopifnot(is.continuous(rnode)) DeleteNetwork(netx) stopSession(sess)
Netica
network.
The function is.NodeRelated()
tests to see if relation
holds between node1
and node2
. The function
GetRelatedNodes
creates a list of all nodes that satisfy the
relation
with any node in nodelist
.
is.NodeRelated(node1, node2, relation = "connected") GetRelatedNodes(nodelist, relation = "connected")
is.NodeRelated(node1, node2, relation = "connected") GetRelatedNodes(nodelist, relation = "connected")
node1 |
An active |
node2 |
Another active |
relation |
A character scalar which should be one of the values: "parents", "children", "ancestors", "descendents" [sic], "connected", "markov_blanket", or "d_connected". Singular forms and modifiers are also allowed, see details. |
nodelist |
A list of active |
These functions are useful for testing the topology of a network.
Each of the functions offers a measure related to the network. The
is.NodeRelated()
form tests the relationship between
node1
and node2
. The function GetRelatedNodes()
returns a list of any nodes for which the relationship holds with any
of the elements of nodelist
. The plural and singular forms of
the relationships can be used with both functions.
"parent"
, "parents"
. True if node1
is a parent
of node2
, or returns a list of parents of the nodes in
nodelist
.
"ancestor"
, "ancestors"
. True if there is a directed
(parent to child) path from node1
to node2
, or returns a
list of ancestors of the nodes in nodelist
.
"child"
, "children"
. True if node1
is a child
of node2
, or returns a list of children of the nodes in
nodelist
.
"descendent"
, "descedents"
[This is the spelling used by
Netica]. True if there is a directed
(parent to child) path from node2
to node1
, or returns a
list of descedants of the nodes in nodelist
.
"connected"
. True if there is a chain (unordered path) from
node1
to node2
, or returns a list of all nodes connected
to any of the nodes in nodelist
.
"markov_blanket"
. The Markov blanket of nodeset
is the
a set of nodes that renders the nodes in nodeset
conditionally
independent of the remaining nodes given the ones in the blanket. The
simple form returns true if node2
is in the Markov blanket of
node1
.
"d_connected"
. The rules for d-connection are somewhat complex
(see Pearl, 1988), but basically node1
and node2
are
d-connected if they are not independent given the current findings.
The function returns true if node1
and node2
are
d-connected or a list of all nodes that are d-connected to the nodes
in nodelist
.
In addition, the relation can be modified in the
GetRelatedNodes()
form by adding one or more modifiers to the
main relation separated by commas. The two that are useful in RNetica
are:
"include_evidence_nodes"
. For the "markov_boundary"
and
"d_connected"
relations indicates whether nodes with findings
should be included in the result (they would normally not be
included in the result).
"exclude_self"
. For the "ancestors"
,
"descendents"
, "connected"
, and "d_connected"
relations, the elements of nodelist
are not initially added to
the result.
For is.NodeRelated()
TRUE
or FALSE
, or NA
if one of the input nodes was not active.
For GetNodeRelated()
a list of NeticaNode
objects which
have the target relationship with one of the nodes in
nodelist
. There may be duplicates in this list.
GetRelatedNodes()
uses GetRelatedNodesMult_bn()
, not
GetRelatedNode_bn()
, but that should not present any serious
issues. Also, it always passes an empty list for the
related_nodes
arguments. Consequently, the "append"
,
"union"
, "intersection"
, and "subtract"
options
don't make much sense. This is only a minor limitation as R provides
similar functions.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: IsNodeRelated_bn(), GetRelatedNodes_bn(), GetRelatedNodesMult_bn()
Pearl, J. (1988). Probabilistic Reasoning in Intelligent Systems. Morgan–Kaufmann.
NeticaNode
, NodeParents()
,
NodeChildren()
, AddLink()
sess <- NeticaSession() startSession(sess) testnet <- CreateNetwork("ABCDEFG", session=sess) ### A D ### \ / \ ### C F - G ### / \ / ### B E A <- NewDiscreteNode(testnet,"A") B <- NewDiscreteNode(testnet,"B") C <- NewDiscreteNode(testnet,"C") D <- NewDiscreteNode(testnet,"D") E <- NewDiscreteNode(testnet,"E") F <- NewDiscreteNode(testnet,"F") G <- NewDiscreteNode(testnet,"G") AddLink(A,C) AddLink(B,C) AddLink(C,D) AddLink(C,E) AddLink(D,F) AddLink(E,F) AddLink(F,G) stopifnot( is.NodeRelated(A,C,"parent"), is.NodeRelated(D,C,"child"), is.NodeRelated(C,G,"ancestor"), is.NodeRelated(E,C,"descendent"), is.NodeRelated(A,B), ## Same as connected is.NodeRelated(D,E,"markov_blanket"), !is.NodeRelated(A,B,"d_connected"), ## No common ancestor is.NodeRelated(D,E,"d_connected") ## Common ancestor ) stopifnot( setequal(GetRelatedNodes(F,"parents"),list(D,E)), setequal(GetRelatedNodes(C,"children"),list(D,E)), setequal(GetRelatedNodes(D,"descendents"),list(D,F,G)), setequal(GetRelatedNodes(E,"ancestors"),list(E,C,A,B)), setequal(GetRelatedNodes(E,"ancestors,exclude_self"), GetRelatedNodes(D,"ancestors,exclude_self")), setequal(GetRelatedNodes(A),list(A,B,C,D,E,F,G)), ##All nodes connected setequal(GetRelatedNodes(D,"markov_blanket"),list(C,E,F)), setequal(GetRelatedNodes(A,"d_connected"),list(A,C,D,E,F,G)) ) DeleteNetwork(testnet) stopSession(sess)
sess <- NeticaSession() startSession(sess) testnet <- CreateNetwork("ABCDEFG", session=sess) ### A D ### \ / \ ### C F - G ### / \ / ### B E A <- NewDiscreteNode(testnet,"A") B <- NewDiscreteNode(testnet,"B") C <- NewDiscreteNode(testnet,"C") D <- NewDiscreteNode(testnet,"D") E <- NewDiscreteNode(testnet,"E") F <- NewDiscreteNode(testnet,"F") G <- NewDiscreteNode(testnet,"G") AddLink(A,C) AddLink(B,C) AddLink(C,D) AddLink(C,E) AddLink(D,F) AddLink(E,F) AddLink(F,G) stopifnot( is.NodeRelated(A,C,"parent"), is.NodeRelated(D,C,"child"), is.NodeRelated(C,G,"ancestor"), is.NodeRelated(E,C,"descendent"), is.NodeRelated(A,B), ## Same as connected is.NodeRelated(D,E,"markov_blanket"), !is.NodeRelated(A,B,"d_connected"), ## No common ancestor is.NodeRelated(D,E,"d_connected") ## Common ancestor ) stopifnot( setequal(GetRelatedNodes(F,"parents"),list(D,E)), setequal(GetRelatedNodes(C,"children"),list(D,E)), setequal(GetRelatedNodes(D,"descendents"),list(D,F,G)), setequal(GetRelatedNodes(E,"ancestors"),list(E,C,A,B)), setequal(GetRelatedNodes(E,"ancestors,exclude_self"), GetRelatedNodes(D,"ancestors,exclude_self")), setequal(GetRelatedNodes(A),list(A,B,C,D,E,F,G)), ##All nodes connected setequal(GetRelatedNodes(D,"markov_blanket"),list(C,E,F)), setequal(GetRelatedNodes(A,"d_connected"),list(A,C,D,E,F,G)) ) DeleteNetwork(testnet) stopSession(sess)
A node in a Bayesian network is deterministic if its value is determined by the states of its parents, that is if all conditional probabilities are 0 or 1.
IsNodeDeterministic(node)
IsNodeDeterministic(node)
node |
An active |
For discrete nodes, this returns TRUE
if all the conditional
probabilities are zero or one. It returns FALSE
otherwise.
TRUE
if the conditional probability table for node
is
deterministic, FALSE
otherwise. If the node is not active, or
there is otherwise an error it returns NA
.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: IsNodeDeterministic_bn()
NeticaNode
, NodeParents()
,
NodeInputNames()
, NodeStates()
sess <- NeticaSession() startSession(sess) ab <- CreateNetwork("AB", session=sess) A <- NewDiscreteNode(ab,"A",c("A1","A2","A3")) B <- NewDiscreteNode(ab,"B",c("B1","B2")) AddLink(A,B) ##Undefined node is not deterministic. stopifnot(!IsNodeDeterministic(A)) NodeProbs(A) <- c(0,1,0) stopifnot(IsNodeDeterministic(A)) NodeProbs(A) <- c(1/3,1/3,1/3) stopifnot(!IsNodeDeterministic(A)) NodeProbs(B) <- rbind(c(0,1), c(0,1), c(1,0)) stopifnot(IsNodeDeterministic(B)) DeleteNetwork(ab) stopSession(sess)
sess <- NeticaSession() startSession(sess) ab <- CreateNetwork("AB", session=sess) A <- NewDiscreteNode(ab,"A",c("A1","A2","A3")) B <- NewDiscreteNode(ab,"B",c("B1","B2")) AddLink(A,B) ##Undefined node is not deterministic. stopifnot(!IsNodeDeterministic(A)) NodeProbs(A) <- c(0,1,0) stopifnot(IsNodeDeterministic(A)) NodeProbs(A) <- c(1/3,1/3,1/3) stopifnot(!IsNodeDeterministic(A)) NodeProbs(B) <- rbind(c(0,1), c(0,1), c(1,0)) stopifnot(IsNodeDeterministic(B)) DeleteNetwork(ab) stopSession(sess)
The Bayesian network, once compiled, gives the joint probability of all nodes in the network given the findings. This function calculates the joint probability over all of the nodes its argument and returns it as an array.
JointProbability(nodelist)
JointProbability(nodelist)
nodelist |
A list of active |
This calculates the joint probability distribution over two, three or
more variables in the same network. Calculating the joint probability
is easy if all of the nodes are in the same clique, so one might want
to use the function MakeCliqueNode(nodelist)
before
compiling the network to force the nodes in the same clique. The
function can calculate the joint probability table for nodes not in
the same clique, it just takes longer.
A multidimensional array given the probabilities of the various
configurations. The dimensions correspond to the variables in
nodelist
, and the dimnames of the result are the result of
sapply(nodelist,NodeStates)
.
One possible use for the joint probability function is to get a joint likelihood over the footprint nodes in an evidence model (see Almond et al, 1999; Almond & Mislevy, 1999). However, Netica currently does not support inserting a likelihood on a clique, just on a single node.
Russell Almond
Almond, R. G. & Mislevy, R. J. (1999) Graphical models and computerized adaptive testing. Applied Psychological Measurement, 23, 223–238.
Almond, R., Herskovits, E., Mislevy, R. J., & Steinberg, L. S. (1999). Transfer of information between system and evidence models. In Artificial Intelligence and Statistics 99, Proceedings (pp. 181–186). Morgan-Kaufmann
http://norsys.com/onLineAPIManual/index.html: JointProbability_bn()
NeticaNode
,NodeBeliefs()
MakeCliqueNode()
, AddLink()
,
JunctionTreeReport()
, MostProbableConfig()
sess <- NeticaSession() startSession(sess) EMSMMotif <- ReadNetworks(system.file("sampleNets","EMSMMotif.dne", package="RNetica"), session=sess) ## Force Skills 1 and 2 into the same clique. Skills12 <- NetworkFindNode(EMSMMotif,c("Skill1","Skill2")) cn <- MakeCliqueNode(Skills12) CompileNetwork(EMSMMotif) ## Prior Joint probability. prior <- JointProbability(Skills12) stopifnot (abs(sum(prior)-1) <.0001) ## Find observable nodes obs <- NetworkNodesInSet(EMSMMotif,"Observable") NodeFinding(obs$Obs1a1) <- "Right" NodeFinding(obs$Obs1a2) <- "Wrong" post <- JointProbability(GetClique(cn)) stopifnot (abs(sum(post)-1) <.0001) DeleteNetwork(EMSMMotif) stopSession(sess)
sess <- NeticaSession() startSession(sess) EMSMMotif <- ReadNetworks(system.file("sampleNets","EMSMMotif.dne", package="RNetica"), session=sess) ## Force Skills 1 and 2 into the same clique. Skills12 <- NetworkFindNode(EMSMMotif,c("Skill1","Skill2")) cn <- MakeCliqueNode(Skills12) CompileNetwork(EMSMMotif) ## Prior Joint probability. prior <- JointProbability(Skills12) stopifnot (abs(sum(prior)-1) <.0001) ## Find observable nodes obs <- NetworkNodesInSet(EMSMMotif,"Observable") NodeFinding(obs$Obs1a1) <- "Right" NodeFinding(obs$Obs1a2) <- "Wrong" post <- JointProbability(GetClique(cn)) stopifnot (abs(sum(post)-1) <.0001) DeleteNetwork(EMSMMotif) stopSession(sess)
The process of compilation transforms the network into a junction tree
– a tree of cliques in the original graph – that is more convenient
computationally. The function JunctionTreeReport(net)
produces
a report on the junction tree. The function
NetworkCompiledSize(net)
reports on the size of the compiled
network. The network must be compiled
(CompileNetwork(net)
must be called) before these
functions are called.
JunctionTreeReport(net) NetworkCompiledSize(net)
JunctionTreeReport(net) NetworkCompiledSize(net)
net |
An active and compiled |
For JunctionTreeReport()
a character vector giving the report,
one element per line.
For NetworkCompiledSize()
a scalar value giving the size of the
network.
Currently, no attempt is made to parse the report, which has a fairly well structured format. Future versions may produce a report object instead.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: ReportJunctionTree_bn(), SizeCompiledNet_bn()
NeticaBN
, CompileNetwork()
,
EliminationOrder()
,
sess <- NeticaSession() startSession(sess) EMSMMotif <- ReadNetworks(system.file("sampleNets","EMSMMotif.dne", package="RNetica"), session=sess) CompileNetwork(EMSMMotif) JunctionTreeReport(EMSMMotif) NetworkCompiledSize(EMSMMotif) DeleteNetwork(EMSMMotif) stopSession(sess)
sess <- NeticaSession() startSession(sess) EMSMMotif <- ReadNetworks(system.file("sampleNets","EMSMMotif.dne", package="RNetica"), session=sess) CompileNetwork(EMSMMotif) JunctionTreeReport(EMSMMotif) NetworkCompiledSize(EMSMMotif) DeleteNetwork(EMSMMotif) stopSession(sess)
This function updates the conditional probabilities associated with
the given list of nodes based on the findings associated with that
node and its parents found in the caseStream
argument, which
should be a CaseStream
object.
LearnCases(caseStream, nodelist, weight = 1)
LearnCases(caseStream, nodelist, weight = 1)
caseStream |
This should be a |
nodelist |
A list of active |
weight |
A multiplier for the weights of the cases in terms of number of observations. Negative weights unlearn previously learned cases. |
This is like calling the function LearnFindings
repeatedly with the values of the nodes set to each of the case rows
in turn. Thus, it updates the conditional probability tables for each
nodes based on observed counts in the case files, taking the current
probability and the NodeExperience
as the prior
distribution.
If the case stream has a column NumCases
, then the weight
assigned to Row is
weight*NumCases[j]
. If the case
stream does not have such a column, then it is treated as if each
column has weight 1. (Among other purposes, this allows case data to
be stored in a compact format where all of the possible cases are
enumerated along with a count of repetitions.) Note that negative
weights will unlearn cases.
This function returns the CaseStream
used in the
analysis. This might have either been passed directly as the
caseStream
argument, or created from the value of the
caseStream
argument. In either case, the stream is closed at
the end of the function.
In version 5.04 of the Netica API, there is a problem with using
Memory Streams that seems to affect the functions
LearnCases
and LearnCPTs
. Until this
problem is fixed, most uses of Memory Streams will require file
streams instead. Write the case file using
write.CaseFile
, and then create a file stream using
CaseFileStream
.
To learn without using the current probabilities as priors, call
DeleteNodeTable
first.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: ReviseCPTsByCaseFile_bn()
NodeExperience
, NodeProbs
,
NodeFinding
, FadeCPT
,
LearnFindings
, DeleteNodeTable
,
LearnCPTs
sess <- NeticaSession() startSession(sess) abb <- CreateNetwork("ABB", session=sess) A <- NewDiscreteNode(abb,"A",c("A1","A2")) B1 <- NewDiscreteNode(abb,"B1",c("B1","B2")) B2 <- NewDiscreteNode(abb,"B2",c("B1","B2")) AddLink(A,B1) AddLink(A,B2) A[] <- c(.5,.5) NodeExperience(A) <- 10 B1["A1"] <- c(.8,.2) B1["A2"] <- c(.2,.8) B2["A1"] <- c(.8,.2) B2["A2"] <- c(.2,.8) NodeExperience(B1) <- c(10,10) NodeExperience(B2) <- c(10,10) casesabb <- data.frame(A=c("A1","A1","A1","A1","A1","A2","A2","A2","A2","A2"), B1=c("B1","B1","B1","B2","B2","B2","B2","B2","B1","B1"), B2=c("B1","B1","B1","B1","B2","B2","B2","B2","B2","B1")) ## LearnCases(casesabb,list(A,B1)) ## There is currently a bug in Netica, so that this function does not ## work with memory streams. As a work around, use proper file streams ## instead. outfile <- tempfile("casesabb",fileext=".cas") write.CaseFile(casesabb,outfile, session=sess) LearnCases(outfile,list(A,B1)) ## Probs for A & B1 modified, but B2 left alone stopifnot( NodeExperience(A)==20, NodeExperience(B1)==c(15,15), NodeExperience(B2)==c(10,10), sum(abs(NodeProbs(A) - .5)) < .001, sum(abs(B1["A1",drop=TRUE] - c(11,4)/15)) < .001, sum(abs(B1["A2",drop=TRUE] - c(4,11)/15)) < .001, sum(abs(B2["A1",drop=TRUE] - c(8,2)/10)) < .001, sum(abs(B2["A2",drop=TRUE] - c(2,8)/10)) < .001 ) DeleteNetwork(abb) stopSession(sess)
sess <- NeticaSession() startSession(sess) abb <- CreateNetwork("ABB", session=sess) A <- NewDiscreteNode(abb,"A",c("A1","A2")) B1 <- NewDiscreteNode(abb,"B1",c("B1","B2")) B2 <- NewDiscreteNode(abb,"B2",c("B1","B2")) AddLink(A,B1) AddLink(A,B2) A[] <- c(.5,.5) NodeExperience(A) <- 10 B1["A1"] <- c(.8,.2) B1["A2"] <- c(.2,.8) B2["A1"] <- c(.8,.2) B2["A2"] <- c(.2,.8) NodeExperience(B1) <- c(10,10) NodeExperience(B2) <- c(10,10) casesabb <- data.frame(A=c("A1","A1","A1","A1","A1","A2","A2","A2","A2","A2"), B1=c("B1","B1","B1","B2","B2","B2","B2","B2","B1","B1"), B2=c("B1","B1","B1","B1","B2","B2","B2","B2","B2","B1")) ## LearnCases(casesabb,list(A,B1)) ## There is currently a bug in Netica, so that this function does not ## work with memory streams. As a work around, use proper file streams ## instead. outfile <- tempfile("casesabb",fileext=".cas") write.CaseFile(casesabb,outfile, session=sess) LearnCases(outfile,list(A,B1)) ## Probs for A & B1 modified, but B2 left alone stopifnot( NodeExperience(A)==20, NodeExperience(B1)==c(15,15), NodeExperience(B2)==c(10,10), sum(abs(NodeProbs(A) - .5)) < .001, sum(abs(B1["A1",drop=TRUE] - c(11,4)/15)) < .001, sum(abs(B1["A2",drop=TRUE] - c(4,11)/15)) < .001, sum(abs(B2["A1",drop=TRUE] - c(8,2)/10)) < .001, sum(abs(B2["A2",drop=TRUE] - c(2,8)/10)) < .001 ) DeleteNetwork(abb) stopSession(sess)
This function updates the conditional probabilities associated with
the given list of nodes based on the findings associated with that
node and its parents found in the caseStream
argument, which
should be a CaseStream
object. Unlike
LearnCases
, these algorithms can support cases with
missing or latent variables.
LearnCPTs(caseStream, nodelist, method = "COUNTING", maxIters = 1000L, maxTol = 1e-06, weight = 1)
LearnCPTs(caseStream, nodelist, method = "COUNTING", maxIters = 1000L, maxTol = 1e-06, weight = 1)
caseStream |
This should be a |
nodelist |
A list of active |
method |
A character scalar giving the name of the method to be used. This should be one of “GRADIENT”, “EM” or “COUNTING” (the default). See details. |
maxIters |
An integer scalar giving the maximum number of interactions for the EM and gradient decent algorithms. |
maxTol |
A real scalar giving the difference in log-likelihood required before the EM or gradient decent algorithms to be considered converged. |
weight |
A multiplier for the weights of the cases in terms of number of observations. Negative weights unlearn previously learned cases. |
This function attempts to update the conditional probability tables of
the nodes named in nodelist
using the data referenced in the
first argument. Three different algorithms are available:
Counting, EM and Gradient Decent. The
Counting algorithm cannot handle cases with missing data or
latent variables in the model. The method
argument determines
which method is used.
The Counting algorithm is the same as the one used in
LearnCases
. Cases where either the parent or the child
variable is missing are ignored when updating the conditional
probability table for the node, that is the neither affect the
NodeExperience
or the NodeProbs
. As a
consequence, models with latent variables cannot be fit with this
algorithm.
The EM is similar to the Counting algorithms, but does more intelligent things with missing observations (particularly, missing parent variables). In particular, the complete data case of the EM algorithm is the same as the counting algorithm.
The Gradient Decent algorithm is an alternative iterative
algorithm. According to the Netica documentation it is similar to
back propagation in neural networks. Again according to Netica, it is
faster than EM, but more likely to find a local maxima. It appears
not to respect prior information about the conditional probability
tables, and it sets the node experience to -Inf
.
Both EM
and Gradient Decent
are an iterative algorithms.
For these algorithms maxIters
gives the maximum number of
iterations, and maxTol
gives the convergence criteria (required
difference in log likelihood). These parameters are ignored for the
Counting algorithm. Currently, Netica gives no indication of
whether the algorithm terminated by achieving convergence (difference
in log likelihood less than maxTol
) or by exceeding
maxIters
. Norsys says they will fix this in an upcoming
release.
If the case stream has a column NumCases
, then the weight
assigned to Row is
weight*NumCases[j]
. If the case
stream does not have such a column, then it is treated as if each
column has weight 1. (Among other purposes, this allows case data to
be stored in a compact format where all of the possible cases are
enumerated along with a count of repetitions.) Note that negative
weights will unlearn cases.
Currently, NULL
is returned. In the future, an object
containing details about the convergence will be returned.
In version 5.04 of the Netica API, there is no indication of whether
the call to LearnCPTs_bn has converged (terminated because the
difference in log likelihood is less than maxTol
) or not
(terminated because the number of iterations exceeded
maxIters
). Norsys has indicated that they will add this
functionality to a later release.
In version 5.04 of the Netica API, there is a problem with using
Memory Streams that seems to affect the functions
LearnCases
and LearnCPTs
. Until this
problem is fixed, most uses of Memory Streams will require file
streams instead. Write the case file using
write.CaseFile
, and then create a file stream using
CaseFileStream
.
The LearnCPTs
function will not update the conditional
probability table of a node unless NodeExperience
has
been set for that node. Instead it will issue a warning and update
the other nodes.
Russell G. Almond
http://norsys.com/onLineAPIManual/index.html: LearnCPTS_bn(), NewLearner_bn(), SetLearnerMaxTol_bn(), SetLearnerMaxTol_bn()
NodeExperience
, NodeProbs
,
NodeFinding
, FadeCPT
,
RetractNetFindings
, LearnFindings
LearnCases
sess <- NeticaSession() startSession(sess) abb <- CreateNetwork("ABB", session=sess) A <- NewDiscreteNode(abb,"A",c("A1","A2")) B1 <- NewDiscreteNode(abb,"B1",c("B1","B2")) B2 <- NewDiscreteNode(abb,"B2",c("B1","B2")) AddLink(A,B1) AddLink(A,B2) A[] <- c(.5,.5) NodeExperience(A) <- 10 B1["A1"] <- c(.8,.2) B1["A2"] <- c(.2,.8) B2["A1"] <- c(.8,.2) B2["A2"] <- c(.2,.8) NodeExperience(B1) <- c(10,10) NodeExperience(B2) <- c(10,10) casesabb <- data.frame(A=c("A1","A1","A1","A1","A1","A2","A2","A2","A2","A2"), B1=c("B1","B1","B1","B2","B2","B2","B2","B2","B1","B1"), B2=c("B1","B1","B1","B1","B2","B2","B2","B2","B2","B1")) ## LearnCPTs(casesabb,list(A,B1)) ## There is currently a bug in Netica, so that this function does not ## work with memory streams. As a work around, use proper file streams ## instead. outfile <- tempfile("casesabb",fileext=".cas") write.CaseFile(casesabb,outfile, session=sess) LearnCPTs(outfile,list(A,B1)) ## Probs for A & B1 modified, but B2 left alone stopifnot( NodeExperience(A)==20, NodeExperience(B1)==c(15,15), NodeExperience(B2)==c(10,10), sum(abs(NodeProbs(A) - .5)) < .001, sum(abs(B1["A1",drop=TRUE] - c(11,4)/15)) < .001, sum(abs(B1["A2",drop=TRUE] - c(4,11)/15)) < .001, sum(abs(B2["A1",drop=TRUE] - c(8,2)/10)) < .001, sum(abs(B2["A2",drop=TRUE] - c(2,8)/10)) < .001 ) ## Missing Data ## NAs in parents affect both parent and child. casesabb1 <- data.frame(A=c("A1","A1","NA","A1","A1","A2","A2","A2","A2","A2"), B1=c("B1","B1","B1","B2","B2","B2","B2","NA","B1","B1"), B2=c("B1","B1","B1","NA","B2","B2","B2","B2","B2","B1")) outfile1 <- tempfile("casesabb1",fileext=".cas") write.CaseFile(casesabb1,outfile1, session=sess) LearnCPTs(outfile1,list(A,B1,B2)) stopifnot( NodeExperience(A)==29, NodeExperience(B1)==c(19,19), NodeExperience(B2)==c(13,15), sum(abs(NodeProbs(A) - c(14,15)/29)) < .001, sum(abs(B1["A1",drop=TRUE] - c(13,6)/19)) < .001, sum(abs(B1["A2",drop=TRUE] - c(6,13)/19)) < .001, sum(abs(B2["A1",drop=TRUE] - c(10,3)/13)) < .001, sum(abs(B2["A2",drop=TRUE] - c(3,12)/15)) < .001 ) DeleteNetwork(abb) #################################### ## Start again with EM learning. abb <- CreateNetwork("ABB", session=sess) A <- NewDiscreteNode(abb,"A",c("A1","A2")) B1 <- NewDiscreteNode(abb,"B1",c("B1","B2")) B2 <- NewDiscreteNode(abb,"B2",c("B1","B2")) AddLink(A,B1) AddLink(A,B2) A[] <- c(.5,.5) NodeExperience(A) <- 10 B1["A1"] <- c(.8,.2) B1["A2"] <- c(.2,.8) B2["A1"] <- c(.8,.2) B2["A2"] <- c(.2,.8) NodeExperience(B1) <- c(10,10) NodeExperience(B2) <- c(10,10) casesabb <- data.frame(A=c("A1","A1","A1","A1","A1","A2","A2","A2","A2","A2"), B1=c("B1","B1","B1","B2","B2","B2","B2","B2","B1","B1"), B2=c("B1","B1","B1","B1","B2","B2","B2","B2","B2","B1")) ## LearnCPTs(casesabb,list(A,B1),method="EM") ## There is currently a bug in Netica, so that this function does not ## work with memory streams. As a work around, use proper file streams ## instead. outfile <- tempfile("casesabb",fileext=".cas") write.CaseFile(casesabb,outfile, session=sess) LearnCPTs(outfile,list(A,B1),method="EM") ## Complete data, this should look identical to the counting case. ## Note that NodeExperience is no longer an integer stopifnot( abs(NodeExperience(A)-20) < .001, sum(abs(NodeExperience(B1)-c(15,15))) < .001, NodeExperience(B2)==c(10,10), sum(abs(NodeProbs(A) - .5)) < .001, sum(abs(B1["A1",drop=TRUE] - c(11,4)/15)) < .001, sum(abs(B1["A2",drop=TRUE] - c(4,11)/15)) < .001, sum(abs(B2["A1",drop=TRUE] - c(8,2)/10)) < .001, sum(abs(B2["A2",drop=TRUE] - c(2,8)/10)) < .001 ) ## Missing Data ## EM deals more intelligently with missing data. casesabb1 <- data.frame(A=c("A1","A1","NA","A1","A1","A2","A2","A2","A2","A2"), B1=c("B1","B1","B1","B2","B2","B2","B2","NA","B1","B1"), B2=c("B1","B1","B1","NA","B2","B2","B2","B2","B2","B1")) outfile1 <- tempfile("casesabb1",fileext=".cas") write.CaseFile(casesabb1,outfile1, session=sess) LearnCPTs(outfile1,list(A,B1,B2),method="EM") stopifnot( NodeExperience(A)>29, NodeExperience(B1)>c(19,19), NodeExperience(B2)>c(13,15) ) ## EM can handle complete latent variable case. casesabb2 <- data.frame(B1=c("B1","B1","B1","B2","B2","B2","B2","NA","B1","B1"), B2=c("B1","B1","B1","NA","B2","B2","B2","B2","B2","B1")) outfile2 <- tempfile("casesabb2",fileext=".cas") write.CaseFile(casesabb1,outfile2, session=sess) LearnCPTs(outfile1,list(A,B1,B2),method="EM") stopifnot( NodeExperience(A)>39, NodeExperience(B1)>c(24,23), NodeExperience(B2)>c(14,20) ) DeleteNetwork(abb) #################################### ## One more time with Gradient Decent learning. abb <- CreateNetwork("ABB", session=sess) A <- NewDiscreteNode(abb,"A",c("A1","A2")) B1 <- NewDiscreteNode(abb,"B1",c("B1","B2")) B2 <- NewDiscreteNode(abb,"B2",c("B1","B2")) AddLink(A,B1) AddLink(A,B2) A[] <- c(.5,.5) NodeExperience(A) <- 10 B1["A1"] <- c(.8,.2) B1["A2"] <- c(.2,.8) B2["A1"] <- c(.8,.2) B2["A2"] <- c(.2,.8) NodeExperience(B1) <- c(10,10) NodeExperience(B2) <- c(10,10) casesabb <- data.frame(A=c("A1","A1","A1","A1","A1","A2","A2","A2","A2","A2"), B1=c("B1","B1","B1","B2","B2","B2","B2","B2","B1","B1"), B2=c("B1","B1","B1","B1","B2","B2","B2","B2","B2","B1")) ## LearnCPTs(casesabb,list(A,B1),method="GRADIENT") ## There is currently a bug in Netica, so that this function does not ## work with memory streams. As a work around, use proper file streams ## instead. outfile <- tempfile("casesabb",fileext=".cas") write.CaseFile(casesabb,outfile, session=sess) LearnCPTs(outfile,list(A,B1),method="GRADIENT") ## Complete data, this should look identical to the counting case. ## Note that NodeExperience is no longer used, and the posterior ## distribution no longer reflects the prior. stopifnot( NodeExperience(B2)==c(10,10), sum(abs(NodeProbs(A) - .5)) < .001, sum(abs(B1["A1",drop=TRUE] - c(3,2)/5)) < .001, sum(abs(B1["A2",drop=TRUE] - c(2,3)/5)) < .001, sum(abs(B2["A1",drop=TRUE] - c(8,2)/10)) < .001, sum(abs(B2["A2",drop=TRUE] - c(2,8)/10)) < .001 ) ## Gradient algorithm sets experience to -infinity, so need to reset. NodeExperience(A) <- 10 NodeExperience(B1) <- c(10,10) NodeExperience(B2) <- c(10,10) ## Missing Data ## GRADIENT deals more intelligently with missing data. casesabb1 <- data.frame(A=c("A1","A1","NA","A1","A1","A2","A2","A2","A2","A2"), B1=c("B1","B1","B1","B2","B2","B2","B2","NA","B1","B1"), B2=c("B1","B1","B1","NA","B2","B2","B2","B2","B2","B1")) outfile1 <- tempfile("casesabb1",fileext=".cas") write.CaseFile(casesabb1,outfile1, session=sess) LearnCPTs(outfile1,list(A,B1,B2),method="GRADIENT") ## Gradient algorithm sets experience to -infinity, so need to reset. NodeExperience(A) <- 10 NodeExperience(B1) <- c(10,10) NodeExperience(B2) <- c(10,10) ## GRADIENT can handle complete latent variable case. casesabb2 <- data.frame(B1=c("B1","B1","B1","B2","B2","B2","B2","NA","B1","B1"), B2=c("B1","B1","B1","NA","B2","B2","B2","B2","B2","B1")) outfile2 <- tempfile("casesabb2",fileext=".cas") write.CaseFile(casesabb1,outfile2, session=sess) LearnCPTs(outfile1,list(A,B1,B2),method="GRADIENT") DeleteNetwork(abb) stopSession(sess)
sess <- NeticaSession() startSession(sess) abb <- CreateNetwork("ABB", session=sess) A <- NewDiscreteNode(abb,"A",c("A1","A2")) B1 <- NewDiscreteNode(abb,"B1",c("B1","B2")) B2 <- NewDiscreteNode(abb,"B2",c("B1","B2")) AddLink(A,B1) AddLink(A,B2) A[] <- c(.5,.5) NodeExperience(A) <- 10 B1["A1"] <- c(.8,.2) B1["A2"] <- c(.2,.8) B2["A1"] <- c(.8,.2) B2["A2"] <- c(.2,.8) NodeExperience(B1) <- c(10,10) NodeExperience(B2) <- c(10,10) casesabb <- data.frame(A=c("A1","A1","A1","A1","A1","A2","A2","A2","A2","A2"), B1=c("B1","B1","B1","B2","B2","B2","B2","B2","B1","B1"), B2=c("B1","B1","B1","B1","B2","B2","B2","B2","B2","B1")) ## LearnCPTs(casesabb,list(A,B1)) ## There is currently a bug in Netica, so that this function does not ## work with memory streams. As a work around, use proper file streams ## instead. outfile <- tempfile("casesabb",fileext=".cas") write.CaseFile(casesabb,outfile, session=sess) LearnCPTs(outfile,list(A,B1)) ## Probs for A & B1 modified, but B2 left alone stopifnot( NodeExperience(A)==20, NodeExperience(B1)==c(15,15), NodeExperience(B2)==c(10,10), sum(abs(NodeProbs(A) - .5)) < .001, sum(abs(B1["A1",drop=TRUE] - c(11,4)/15)) < .001, sum(abs(B1["A2",drop=TRUE] - c(4,11)/15)) < .001, sum(abs(B2["A1",drop=TRUE] - c(8,2)/10)) < .001, sum(abs(B2["A2",drop=TRUE] - c(2,8)/10)) < .001 ) ## Missing Data ## NAs in parents affect both parent and child. casesabb1 <- data.frame(A=c("A1","A1","NA","A1","A1","A2","A2","A2","A2","A2"), B1=c("B1","B1","B1","B2","B2","B2","B2","NA","B1","B1"), B2=c("B1","B1","B1","NA","B2","B2","B2","B2","B2","B1")) outfile1 <- tempfile("casesabb1",fileext=".cas") write.CaseFile(casesabb1,outfile1, session=sess) LearnCPTs(outfile1,list(A,B1,B2)) stopifnot( NodeExperience(A)==29, NodeExperience(B1)==c(19,19), NodeExperience(B2)==c(13,15), sum(abs(NodeProbs(A) - c(14,15)/29)) < .001, sum(abs(B1["A1",drop=TRUE] - c(13,6)/19)) < .001, sum(abs(B1["A2",drop=TRUE] - c(6,13)/19)) < .001, sum(abs(B2["A1",drop=TRUE] - c(10,3)/13)) < .001, sum(abs(B2["A2",drop=TRUE] - c(3,12)/15)) < .001 ) DeleteNetwork(abb) #################################### ## Start again with EM learning. abb <- CreateNetwork("ABB", session=sess) A <- NewDiscreteNode(abb,"A",c("A1","A2")) B1 <- NewDiscreteNode(abb,"B1",c("B1","B2")) B2 <- NewDiscreteNode(abb,"B2",c("B1","B2")) AddLink(A,B1) AddLink(A,B2) A[] <- c(.5,.5) NodeExperience(A) <- 10 B1["A1"] <- c(.8,.2) B1["A2"] <- c(.2,.8) B2["A1"] <- c(.8,.2) B2["A2"] <- c(.2,.8) NodeExperience(B1) <- c(10,10) NodeExperience(B2) <- c(10,10) casesabb <- data.frame(A=c("A1","A1","A1","A1","A1","A2","A2","A2","A2","A2"), B1=c("B1","B1","B1","B2","B2","B2","B2","B2","B1","B1"), B2=c("B1","B1","B1","B1","B2","B2","B2","B2","B2","B1")) ## LearnCPTs(casesabb,list(A,B1),method="EM") ## There is currently a bug in Netica, so that this function does not ## work with memory streams. As a work around, use proper file streams ## instead. outfile <- tempfile("casesabb",fileext=".cas") write.CaseFile(casesabb,outfile, session=sess) LearnCPTs(outfile,list(A,B1),method="EM") ## Complete data, this should look identical to the counting case. ## Note that NodeExperience is no longer an integer stopifnot( abs(NodeExperience(A)-20) < .001, sum(abs(NodeExperience(B1)-c(15,15))) < .001, NodeExperience(B2)==c(10,10), sum(abs(NodeProbs(A) - .5)) < .001, sum(abs(B1["A1",drop=TRUE] - c(11,4)/15)) < .001, sum(abs(B1["A2",drop=TRUE] - c(4,11)/15)) < .001, sum(abs(B2["A1",drop=TRUE] - c(8,2)/10)) < .001, sum(abs(B2["A2",drop=TRUE] - c(2,8)/10)) < .001 ) ## Missing Data ## EM deals more intelligently with missing data. casesabb1 <- data.frame(A=c("A1","A1","NA","A1","A1","A2","A2","A2","A2","A2"), B1=c("B1","B1","B1","B2","B2","B2","B2","NA","B1","B1"), B2=c("B1","B1","B1","NA","B2","B2","B2","B2","B2","B1")) outfile1 <- tempfile("casesabb1",fileext=".cas") write.CaseFile(casesabb1,outfile1, session=sess) LearnCPTs(outfile1,list(A,B1,B2),method="EM") stopifnot( NodeExperience(A)>29, NodeExperience(B1)>c(19,19), NodeExperience(B2)>c(13,15) ) ## EM can handle complete latent variable case. casesabb2 <- data.frame(B1=c("B1","B1","B1","B2","B2","B2","B2","NA","B1","B1"), B2=c("B1","B1","B1","NA","B2","B2","B2","B2","B2","B1")) outfile2 <- tempfile("casesabb2",fileext=".cas") write.CaseFile(casesabb1,outfile2, session=sess) LearnCPTs(outfile1,list(A,B1,B2),method="EM") stopifnot( NodeExperience(A)>39, NodeExperience(B1)>c(24,23), NodeExperience(B2)>c(14,20) ) DeleteNetwork(abb) #################################### ## One more time with Gradient Decent learning. abb <- CreateNetwork("ABB", session=sess) A <- NewDiscreteNode(abb,"A",c("A1","A2")) B1 <- NewDiscreteNode(abb,"B1",c("B1","B2")) B2 <- NewDiscreteNode(abb,"B2",c("B1","B2")) AddLink(A,B1) AddLink(A,B2) A[] <- c(.5,.5) NodeExperience(A) <- 10 B1["A1"] <- c(.8,.2) B1["A2"] <- c(.2,.8) B2["A1"] <- c(.8,.2) B2["A2"] <- c(.2,.8) NodeExperience(B1) <- c(10,10) NodeExperience(B2) <- c(10,10) casesabb <- data.frame(A=c("A1","A1","A1","A1","A1","A2","A2","A2","A2","A2"), B1=c("B1","B1","B1","B2","B2","B2","B2","B2","B1","B1"), B2=c("B1","B1","B1","B1","B2","B2","B2","B2","B2","B1")) ## LearnCPTs(casesabb,list(A,B1),method="GRADIENT") ## There is currently a bug in Netica, so that this function does not ## work with memory streams. As a work around, use proper file streams ## instead. outfile <- tempfile("casesabb",fileext=".cas") write.CaseFile(casesabb,outfile, session=sess) LearnCPTs(outfile,list(A,B1),method="GRADIENT") ## Complete data, this should look identical to the counting case. ## Note that NodeExperience is no longer used, and the posterior ## distribution no longer reflects the prior. stopifnot( NodeExperience(B2)==c(10,10), sum(abs(NodeProbs(A) - .5)) < .001, sum(abs(B1["A1",drop=TRUE] - c(3,2)/5)) < .001, sum(abs(B1["A2",drop=TRUE] - c(2,3)/5)) < .001, sum(abs(B2["A1",drop=TRUE] - c(8,2)/10)) < .001, sum(abs(B2["A2",drop=TRUE] - c(2,8)/10)) < .001 ) ## Gradient algorithm sets experience to -infinity, so need to reset. NodeExperience(A) <- 10 NodeExperience(B1) <- c(10,10) NodeExperience(B2) <- c(10,10) ## Missing Data ## GRADIENT deals more intelligently with missing data. casesabb1 <- data.frame(A=c("A1","A1","NA","A1","A1","A2","A2","A2","A2","A2"), B1=c("B1","B1","B1","B2","B2","B2","B2","NA","B1","B1"), B2=c("B1","B1","B1","NA","B2","B2","B2","B2","B2","B1")) outfile1 <- tempfile("casesabb1",fileext=".cas") write.CaseFile(casesabb1,outfile1, session=sess) LearnCPTs(outfile1,list(A,B1,B2),method="GRADIENT") ## Gradient algorithm sets experience to -infinity, so need to reset. NodeExperience(A) <- 10 NodeExperience(B1) <- c(10,10) NodeExperience(B2) <- c(10,10) ## GRADIENT can handle complete latent variable case. casesabb2 <- data.frame(B1=c("B1","B1","B1","B2","B2","B2","B2","NA","B1","B1"), B2=c("B1","B1","B1","NA","B2","B2","B2","B2","B2","B1")) outfile2 <- tempfile("casesabb2",fileext=".cas") write.CaseFile(casesabb1,outfile2, session=sess) LearnCPTs(outfile1,list(A,B1,B2),method="GRADIENT") DeleteNetwork(abb) stopSession(sess)
This function updates the conditional probabilities associated with
the given list of nodes based on the findings associated with that
node and its parents. Before calling this function the findings to be
learned should be set using NodeFinding
.
LearnFindings(nodes, weight = 1)
LearnFindings(nodes, weight = 1)
nodes |
A list of active |
weight |
The weight of the current observation in terms of number of observations. Negative weights unlearn previously learned cases. |
For the purposes of this function, Netica regards the probabilities in
Row of the CPT for each selected node as having an independent
Dirichlet distribution with parameters
where
is the probability
associated with State
in Row
and
is the
experience associated with Row
.
If LearnFindings
is called on a node which is currently
instantiated to State and whose parents are currently
instantiated to the configuration which selects Row
of the
table, then
and
with all other values remaining the same. The new
conditional probabilities are
.
The function FadeCPT
is often used between calls to
LearnFindings
to down weight old cases when the conditional
probabilities are thought to be changing slowly over time.
This returns the list of nodes whose conditional probability tables have been modified.
Do not confuse this function with NodeFinding
.
NodeFinding
instantiates a node and updates all of the other
beliefs associated with a node to reflect the new evidence.
LearnFindings
incorporates the current case (the currently
instantiated set of findings) into the CPTs for the nodes.
The LearnFindings
function will not update the conditional
probability table of a node unless NodeExperience
has
been set for that node. Instead it will issue a warning and update
the other nodes.
Russell G. Almond
http://norsys.com/onLineAPIManual/index.html: ReviseCPTsByFindings_bn()
NodeExperience
, NodeProbs
,
NodeFinding
, FadeCPT
,
RetractNetFindings
, LearnCases
,
LearnCPTs
sess <- NeticaSession() startSession(sess) abb <- CreateNetwork("ABB", session=sess) A <- NewDiscreteNode(abb,"A",c("A1","A2")) B1 <- NewDiscreteNode(abb,"B1",c("B1","B2")) B2 <- NewDiscreteNode(abb,"B2",c("B1","B2")) AddLink(A,B1) AddLink(A,B2) A[] <- c(.5,.5) NodeExperience(A) <- 10 B1["A1"] <- c(.8,.2) B1["A2"] <- c(.2,.8) B2["A1"] <- c(.8,.2) B2["A2"] <- c(.2,.8) NodeExperience(B1) <- c(10,10) NodeExperience(B2) <- c(10,10) ## First Case NodeFinding(A) <- "A1" NodeFinding(B1) <- "B1" NodeFinding(B2) <- "B2" LearnFindings(list(A,B1)) ## Probs for A & B1 modified, but B2 left alone stopifnot( NodeExperience(A)==11, NodeExperience(B1)==c(11,10), NodeExperience(B2)==c(10,10), sum(abs(NodeProbs(A) - c(6,5)/11)) < .001, sum(abs(B1["A1",drop=TRUE] - c(9,2)/11)) < .001, sum(abs(B1["A2",drop=TRUE] - c(2,8)/10)) < .001, sum(abs(B2["A1",drop=TRUE] - c(8,2)/10)) < .001, sum(abs(B2["A2",drop=TRUE] - c(2,8)/10)) < .001 ) ## Second Case RetractNetFindings(abb) NodeFinding(A) <- "A2" NodeFinding(B1) <- "B1" NodeFinding(B2) <- "B1" LearnFindings(list(A,B1)) ## Probs for A & B1 modified, but B2 left alone stopifnot( NodeExperience(A)==12, NodeExperience(B1)==c(11,11), NodeExperience(B2)==c(10,10), sum(abs(NodeProbs(A) - c(6,6)/12)) < .001, sum(abs(B1["A1",drop=TRUE] - c(9,2)/11)) < .001, sum(abs(B1["A2",drop=TRUE] - c(3,8)/11)) < .001, sum(abs(B2["A1",drop=TRUE] - c(8,2)/10)) < .001, sum(abs(B2["A2",drop=TRUE] - c(2,8)/10)) < .001 ) ## Retract Case 2 LearnFindings(list(A,B1),-1) ## Back to where we were before Case 1 stopifnot( NodeExperience(A)==11, NodeExperience(B1)==c(11,10), NodeExperience(B2)==c(10,10), sum(abs(NodeProbs(A) - c(6,5)/11)) < .001, sum(abs(B1["A1",drop=TRUE] - c(9,2)/11)) < .001, sum(abs(B1["A2",drop=TRUE] - c(2,8)/10)) < .001, sum(abs(B2["A1",drop=TRUE] - c(8,2)/10)) < .001, sum(abs(B2["A2",drop=TRUE] - c(2,8)/10)) < .001 ) DeleteNetwork(abb) stopSession(sess)
sess <- NeticaSession() startSession(sess) abb <- CreateNetwork("ABB", session=sess) A <- NewDiscreteNode(abb,"A",c("A1","A2")) B1 <- NewDiscreteNode(abb,"B1",c("B1","B2")) B2 <- NewDiscreteNode(abb,"B2",c("B1","B2")) AddLink(A,B1) AddLink(A,B2) A[] <- c(.5,.5) NodeExperience(A) <- 10 B1["A1"] <- c(.8,.2) B1["A2"] <- c(.2,.8) B2["A1"] <- c(.8,.2) B2["A2"] <- c(.2,.8) NodeExperience(B1) <- c(10,10) NodeExperience(B2) <- c(10,10) ## First Case NodeFinding(A) <- "A1" NodeFinding(B1) <- "B1" NodeFinding(B2) <- "B2" LearnFindings(list(A,B1)) ## Probs for A & B1 modified, but B2 left alone stopifnot( NodeExperience(A)==11, NodeExperience(B1)==c(11,10), NodeExperience(B2)==c(10,10), sum(abs(NodeProbs(A) - c(6,5)/11)) < .001, sum(abs(B1["A1",drop=TRUE] - c(9,2)/11)) < .001, sum(abs(B1["A2",drop=TRUE] - c(2,8)/10)) < .001, sum(abs(B2["A1",drop=TRUE] - c(8,2)/10)) < .001, sum(abs(B2["A2",drop=TRUE] - c(2,8)/10)) < .001 ) ## Second Case RetractNetFindings(abb) NodeFinding(A) <- "A2" NodeFinding(B1) <- "B1" NodeFinding(B2) <- "B1" LearnFindings(list(A,B1)) ## Probs for A & B1 modified, but B2 left alone stopifnot( NodeExperience(A)==12, NodeExperience(B1)==c(11,11), NodeExperience(B2)==c(10,10), sum(abs(NodeProbs(A) - c(6,6)/12)) < .001, sum(abs(B1["A1",drop=TRUE] - c(9,2)/11)) < .001, sum(abs(B1["A2",drop=TRUE] - c(3,8)/11)) < .001, sum(abs(B2["A1",drop=TRUE] - c(8,2)/10)) < .001, sum(abs(B2["A2",drop=TRUE] - c(2,8)/10)) < .001 ) ## Retract Case 2 LearnFindings(list(A,B1),-1) ## Back to where we were before Case 1 stopifnot( NodeExperience(A)==11, NodeExperience(B1)==c(11,10), NodeExperience(B2)==c(10,10), sum(abs(NodeProbs(A) - c(6,5)/11)) < .001, sum(abs(B1["A1",drop=TRUE] - c(9,2)/11)) < .001, sum(abs(B1["A2",drop=TRUE] - c(2,8)/10)) < .001, sum(abs(B2["A1",drop=TRUE] - c(8,2)/10)) < .001, sum(abs(B2["A2",drop=TRUE] - c(2,8)/10)) < .001 ) DeleteNetwork(abb) stopSession(sess)
When a junction tree is compiled, if the nodes are in the same clique,
it is easier to calculate their joint probability. The function
MakeCliqueNode(nodelist)
forces the nodes in nodelist
by
making a special one state clique node with all of the nodes in
nodelist
as a parent.
MakeCliqueNode(nodelist) is.CliqueNode(x) GetClique(cliquenode)
MakeCliqueNode(nodelist) is.CliqueNode(x) GetClique(cliquenode)
nodelist |
A list of active |
x |
An object to be tested to see if it is a clique node. |
cliquenode |
A |
It is substantially easier to calculate the joint probability of a
number of nodes if they are all in the same clique (see
JointProbability(nodelist)
. If it is known that
such a query will be common, the analyst can take steps to force the
nodes into the same clique if required. The Student Model/Evidence
Model algorithm of Almond and Mislevy (1999) also requires that the
student model variables that are referenced in an evidence model all
be in the same clique (although this algorithm is not currently
supported by Netica).
A node and its parents is always a clique or a subset of a clique in
the junction tree (see CompileNetwork()
or
JunctionTreeReport()
). This function forces nodes into
the same clique by creating a new CliqueNode
and making all of
the nodes in nodelist
parents of the new node.
The CliqueNode
is a subclass of
NeticaNode
. It has
a number of special features. It's name is always “Clique”
followed by a number. It only has one state, and it has a special
"clique"
field which records the nodelist
used to
create it. The function is.CliqueNode()
tests a node to see if
it is a clique node, and the function GetClique(node)
retrieves
the nodelist
. (This should not be set manually).
The CliqueNode
objects should, for the most part, behave like
regular nodes. However, it is almost certainly a mistake to try and
set findings on a CliqueNode
.
The function MakeCliqueNode(nodelist)
returns a new
CliqueNode
object whose parents are the variables
in nodelist
. This behaves in most respects like an ordinary
node, but it would almost certainly be a mistake to try and enter
findings for this node. In particular, deleting the clique node will
no longer constrain its parents to be in the same clique (although
other connections in the network may cause the nodes to be placed in
the same clique).
The function is.CliqueNode(x)
returns a logical value which is
true if x
is a clique node.
The function GetClique(node)
returns the nodelist
used
to create the clique node.
Clique nodes only last for the R session that was used to create
them. After that, they will appear like ordinary nodes. They will
still be present in the network, but the special "clique"
attribute will be lost.
Currently Netica only allows virtual evidence at the node level
(NodeLikelihood()
). I'm lobbying to get Netica to
support it at the clique level as well. At which point, this function
becomes extremely useful.
Russell Almond
Almond, R. G. & Mislevy, R. J. (1999) Graphical models and computerized adaptive testing. Applied Psychological Measurement, 23, 223-238.
Almond, R., Herskovits, E., Mislevy, R. J., & Steinberg, L. S. (1999). Transfer of information between system and evidence models. In Artificial Intelligence and Statistics 99, Proceedings (pp. 181–186). Morgan-Kaufmann
http://norsys.com/onLineAPIManual/index.html:
See the NeticaEx function FormCliqueWith
is the documentation
for JointProbability_bn()
CliqueNode
, NeticaNode
,
JointProbability()
, AddLink()
,
JunctionTreeReport()
sess <- NeticaSession() startSession(sess) EMSMSystem <- ReadNetworks(system.file("sampleNets","System.dne", package="RNetica"), session=sess) CompileNetwork(EMSMSystem) ## Note that Skill1 and Skill2 are in different cliques JunctionTreeReport(EMSMSystem) Skills12 <- NetworkFindNode(EMSMSystem,c("Skill1","Skill2")) cn <- MakeCliqueNode(Skills12) cnclique <- GetClique(cn) stopifnot( is.CliqueNode(cn), setequal(sapply(cnclique,NodeName),sapply(Skills12,NodeName)) ) CompileNetwork(EMSMSystem) ## Note that Skill1 and Skill2 are in different cliques JunctionTreeReport(EMSMSystem) DeleteNodes(cn) ## This clears the clique. DeleteNetwork(EMSMSystem) stopSession(sess)
sess <- NeticaSession() startSession(sess) EMSMSystem <- ReadNetworks(system.file("sampleNets","System.dne", package="RNetica"), session=sess) CompileNetwork(EMSMSystem) ## Note that Skill1 and Skill2 are in different cliques JunctionTreeReport(EMSMSystem) Skills12 <- NetworkFindNode(EMSMSystem,c("Skill1","Skill2")) cn <- MakeCliqueNode(Skills12) cnclique <- GetClique(cn) stopifnot( is.CliqueNode(cn), setequal(sapply(cnclique,NodeName),sapply(Skills12,NodeName)) ) CompileNetwork(EMSMSystem) ## Note that Skill1 and Skill2 are in different cliques JunctionTreeReport(EMSMSystem) DeleteNodes(cn) ## This clears the clique. DeleteNetwork(EMSMSystem) stopSession(sess)
"MemoryCaseStream"
This object is subclass of CaseStream
so it is a
wrapper around a Netica stream which is used to read/write cases. In
this subclass, the case stream is associated with a data frame
containing the dase file information. The function
CaseMemoryStream
is the constructor. the case stream is
associated with a memory buffer that corresponds to an R
data.frame
object. The function
MemoryStreamContents
accesses the contents as a data
frame.
A Netica case file has a format that very much resembles the output of
write.table
. The first row is a header row, which
contains the names of the variables, the second and subsequent rows
contain a set of findings: an assignment of values to the nodes
indicated in the columns. There are no row numbers, and the separator
and missing value codes are the values of
CaseFileDelimiter()
, and
CaseFileMissingCode()
respectively.
In addition to columns representing variables, two special columns are
allowed. The column named “IDnum”, if present should contain
integers which correspond to ID numbers for the cases (this correspond
to the id
argument of WriteFindings
). The column
named “NumCases” should contain number values and this allows
rows to be differentially weighted (this correspond to the freq
argument of WriteFindings
).
A simple way to convert a data frame into a set of cases for use with
various Netica functions that use cases would be to write the data
frame to a file of the proper format, and then create a
CaseFileStream
on the just written file. The
MemoryCaseStream
shortcuts that process by writing the data
frame to a memory buffer and then creating a stream around the memory
buffer. Like the FileCaseStream
, the
MemoryCaseStream
is a subclass of
CaseStream
and follows the same conventions.
The function CaseMemoryStream
opens a new memory stream using
data.frame
as the source. If data.frame
is NULL
a new memory stream for writing is created. The function
CloseCaseStream
closes an open case stream (and is harmless if
the stream is already closed. Although RNetica tries to close open
case streams when they are garbage collected, users should not count
on this behavior and should close them manually. Also be aware that
all case streams are automatically closed when R is closes or RNetica
is unloaded. The function isCaseStreamOpen
tests to see if the
stream is open or closed. The function OpenCaseStream
if
called on a closed MemoryCaseStream
will reopen the stream in Netica
using the current value of MemoryStreamContents
as the
source. (If called on an open stream it will do nothing but issue a
warning).
The function getCaseStreamDataFrameName
provides the value of
label
when the stream was created.
Class "CaseStream"
, directly.
All reference classes extend and inherit methods from
"envRefClass"
. Note that because this is a reference
class unlike traditional S3 and S4 classes it can be destructively
modified. Also fields (slots) are accessed using the ‘$’
operator.
In version 5.04 of the Netica API, there is a problem with using
Memory Streams that seems to affect the functions
LearnCases
and LearnCPTs
. Until this
problem is fixed, most uses of Memory Streams will require file
streams instead. Write the case file using
write.CaseFile
, and then create a file stream using
CaseFileStream
.
Note these should be regarded as read-only from user code.
Name
:Object of class character
identifier for
stream. Default is the expression used to reference the data.
Session
:Object of class NeticaSession
Session
:Object of class NeticaSession
a link
to the session in which this case stream was created.
Netica_Case_Stream
:Object of class externalptr
a pointer to the case stream in Netica memory.
Case_Stream_Position
:Object of class integer
the number of the last read/writen record. This is NA
if
the end of the file has been reached.
Case_Stream_Lastid
:Object of class integer
the
ID number of the last read/written record.
Case_Stream_Lastfreq
:Object of class numeric
giving the frequence of the last read/written record. This is
used as a weight in learning applications.
Case_Stream_DataFrameName
:Object of class
character
giving the expression used for the data frame.
Case_Stream_DataFrame
:Object of class
data.frame
or NULL
the data object that is the
contents of the buffer, or NULL
if the stream was created
for writing.
Case_Stream_Buffer
:Object of class externalptr
used for an R-side string buffer, currently not used.
open()
:Opens a connection too the file in Netica.
show()
:Provides a description of the field
initialize(Name, Session, Case_Stream_Path, ...)
:internal constructor; user code should use CaseFileStream
.
The following methods are inherited (from CaseStream
):
close ("CaseStream"), isActive ("CaseStream"), isOpen ("CaseStream"),
show ("CaseStream"), clearErrors ("CaseStream"), reportErrors
("CaseStream"), signalErrors ("CaseStream"), initialize ("CaseStream")
In version 0.5 of RNetica, this class was renamed. It is now called
MemoryCaseStream
and the constructor is called
CaseMemoryStream
(while previously the class and the
constructore had the same name). This matches the usage of
FileCaseStream
and its constructor
CaseFileStream
. This is not likely to be a problem as
memory streams are not working well.
MemoryCaseStreams
are most useful for small to medium size data
frames. Larger data frames are probably better handled through case
files.
Internally, a weak reference system is used to keep a list of Netica stream objects which need to be closed when RNetica is unloaded. Stream objects should also be forced closed when garbage collected. The weak reference system is somewhat experimental, so well designed code should manually close the streams when the program is through with it.
Stream objects are fragile, and will not survive saving and restoring
an R session. However, the object retains information about itself,
so that calling OpenCaseStream
on the saved object, should
reopen the stream. Note that any position information will be lost.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: NewMemoryStream_ns(), http://homepage.stat.uiowa.edu/~luke/R/references/weakfinex.html
See CaseStream
for the superclass and
FileCaseStream
for a sibling class.
The function CaseMemoryStream
is the constructor.
CaseFileDelimiter
, CaseFileMissingCode
,
WriteFindings
, ReadFindings
,
MemoryStreamContents
sess <- NeticaSession() startSession(sess) abc <- CreateNetwork("ABC", session=sess) A <- NewDiscreteNode(abc,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc,"C",c("C1","C2")) AddLink(A,B) AddLink(A,C) AddLink(B,C) ## This is the file written in CaseFileStream help. casefile <- system.file("testData","abctestcases.cas", package="RNetica") CaseFileDelimiter("\t", session=sess) CaseFileMissingCode("*", session=sess) cases <- read.CaseFile(casefile, session=sess) memstream <- CaseMemoryStream(cases, session=sess) ##Case 1 memstream <- ReadFindings(list(A,B,C),memstream,"FIRST") stopifnot(NodeFinding(A) == "A1", NodeFinding(B) == "B1", NodeFinding(C) == "C1", getCaseStreamLastId(memstream)==1001, abs(getCaseStreamLastFreq(memstream)-1.0) <.0001) ##Case 2 memstream <- ReadFindings(list(A,B,C),memstream,"NEXT") stopifnot(NodeFinding(A) == "A2", NodeFinding(B) == "B2", NodeFinding(C) == "C2", getCaseStreamLastId(memstream)==1002, abs(getCaseStreamLastFreq(memstream)-2.0) <.0001) ##Case 3 memstream <- ReadFindings(list(A,B,C),memstream,"NEXT") stopifnot(NodeFinding(A) == "A3", NodeFinding(B) == "B3", NodeFinding(C) == "@NO FINDING", getCaseStreamLastId(memstream)==1003, abs(getCaseStreamLastFreq(memstream)-1.0) <.0001) ## EOF memstream <- ReadFindings(list(A,B,C),memstream,"NEXT") stopifnot (is.na(getCaseStreamPos(memstream))) ##Clean Up CloseCaseStream(memstream) DeleteNetwork(abc) stopSession(sess)
sess <- NeticaSession() startSession(sess) abc <- CreateNetwork("ABC", session=sess) A <- NewDiscreteNode(abc,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc,"C",c("C1","C2")) AddLink(A,B) AddLink(A,C) AddLink(B,C) ## This is the file written in CaseFileStream help. casefile <- system.file("testData","abctestcases.cas", package="RNetica") CaseFileDelimiter("\t", session=sess) CaseFileMissingCode("*", session=sess) cases <- read.CaseFile(casefile, session=sess) memstream <- CaseMemoryStream(cases, session=sess) ##Case 1 memstream <- ReadFindings(list(A,B,C),memstream,"FIRST") stopifnot(NodeFinding(A) == "A1", NodeFinding(B) == "B1", NodeFinding(C) == "C1", getCaseStreamLastId(memstream)==1001, abs(getCaseStreamLastFreq(memstream)-1.0) <.0001) ##Case 2 memstream <- ReadFindings(list(A,B,C),memstream,"NEXT") stopifnot(NodeFinding(A) == "A2", NodeFinding(B) == "B2", NodeFinding(C) == "C2", getCaseStreamLastId(memstream)==1002, abs(getCaseStreamLastFreq(memstream)-2.0) <.0001) ##Case 3 memstream <- ReadFindings(list(A,B,C),memstream,"NEXT") stopifnot(NodeFinding(A) == "A3", NodeFinding(B) == "B3", NodeFinding(C) == "@NO FINDING", getCaseStreamLastId(memstream)==1003, abs(getCaseStreamLastFreq(memstream)-1.0) <.0001) ## EOF memstream <- ReadFindings(list(A,B,C),memstream,"NEXT") stopifnot (is.na(getCaseStreamPos(memstream))) ##Clean Up CloseCaseStream(memstream) DeleteNetwork(abc) stopSession(sess)
This function returns the contents of a
MemoryCaseStream
's internal buffer as a
data.frame
. Alternatively, it sets the contents
of the buffer to a given data frame.
MemoryStreamContents(stream) MemoryStreamContents(stream) <- value
MemoryStreamContents(stream) MemoryStreamContents(stream) <- value
stream |
A |
value |
Either a data frame giving the new value (see details),
or else |
A set of cases for a Netica network corresponds to a
data.frame
. The columns represent nodes in the graph, and the
values in that particular column correspond to findings for that node:
a particular instantiation for that state, with a value of NA
if the state of that node is unknown.
In addition to columns representing variables, two special columns are
allowed. The column named “IDnum”, if present should contain
integers which correspond to ID numbers for the cases (this correspond
to the id
argument of WriteFindings
). The column
named “NumCases” should contain number values and this allows
rows to be differentially weighted (this correspond to the freq
argument of WriteFindings
).
A MemoryCaseStream
contains an R data frame
object written out in string form. This function converts between the
internal string object and the data frame representation. When called
as MemoryStreamContents(stream)
it reads the current value of
the stream and converts it to a data frame. When called as setter
function, it converts the value into a string and focuses the
MemoryCaseStream
object on this string.
Setting the contents to NULL
creates a new empty stream buffer
inside of the stream object. This is useful for creating a blank
buffer for writing cases.
The code MemoryCaseStream
object maintains a cached copy of the
data frame associated with the memory stream. Calling the function in
either the setter or getter form updates that cache. Calling this
function when the stream is closed, will access the cached copy. In
the case of the setter form, this will updated the cached value, and
if the stream is reopened, it will focus on the new cached value.
Note that if the stream is closed before MemoryStreamContents
is called, then the value returned will be the cached value created
when MemoryStreamContents
was last called, or when the stream
is opened.
A data frame which corresponds to the contents of the stream buffer,
or NULL
if the stream buffer is empty.
The cached value of the stream can be accessed with the expression
stream$Case_Stream_DataFrame
. While it is almost
certainly a mistake to set this value directly, there may be
situations (e.g., avoiding duplicating the data frame when the stream
is essentially open for reading only) where it is useful. On the
other hand, there may be situations where it is useful to read the
cached value without forcing a reread of the memory buffer.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: SetStreamContents_ns(),
CaseFileDelimiter
, CaseFileMissingCode
,
WriteFindings
, ReadFindings
,
MemoryCaseStream
, CaseStream
sess <- NeticaSession() startSession(sess) casefile <- system.file("testData","abctestcases.cas", package="RNetica") CaseFileDelimiter("\t", session=sess) CaseFileMissingCode("*", session=sess) cases <- read.CaseFile(casefile, session=sess) memstream <- CaseMemoryStream(cases, session=sess) ## Should be the same as cases stopifnot(all.equal(MemoryStreamContents(memstream),cases)) MemoryStreamContents(memstream) <- cases CloseCaseStream(memstream) ## Don't forget to read off the value ## first if needed before closing. stopifnot(!isCaseStreamOpen(memstream)) ## This should return the cached value. MemoryStreamContents(memstream) ## Will clear stream when next open MemoryStreamContents(memstream) <- NULL OpenCaseStream(memstream) stopifnot(is.null(MemoryStreamContents(memstream))) CloseCaseStream(memstream) ## Second test, do this from scratch. casesabb <- data.frame(IDnum=1001:1010,NumCases=rep(1,10), A=c("A1","A1","A1","A1","A1","A2","A2","A2","A2","A2"), B1=c("B1","B1","B1","B2","B2","B2","B2","B2","B1","B1"), B2=c("B1","B1","B1","B1","B2","B2","B2","B2","B2","B1")) abbstream <- CaseMemoryStream(casesabb, session=sess) MemoryStreamContents(abbstream) CloseCaseStream(abbstream) abb <- CreateNetwork("ABB", session=sess) A <- NewDiscreteNode(abb,"A",c("A1","A2")) B1 <- NewDiscreteNode(abb,"B1",c("B1","B2")) B2 <- NewDiscreteNode(abb,"B2",c("B1","B2")) AddLink(A,B1) AddLink(A,B2) A[] <- c(.5,.5) NodeExperience(A) <- 10 B1["A1"] <- c(.8,.2) B1["A2"] <- c(.2,.8) B2["A1"] <- c(.8,.2) B2["A2"] <- c(.2,.8) NodeExperience(B1) <- c(10,10) NodeExperience(B2) <- c(10,10) abbstream <- CaseMemoryStream(casesabb, session=sess) ## This does not appear to work correctly abbstream <- ReadFindings(list(A,B1,B2),abbstream,"FIRST") NodeFinding(A) NodeFinding(B1) NodeFinding(B2) CloseCaseStream(abbstream) DeleteNetwork(abb) stopSession(sess)
sess <- NeticaSession() startSession(sess) casefile <- system.file("testData","abctestcases.cas", package="RNetica") CaseFileDelimiter("\t", session=sess) CaseFileMissingCode("*", session=sess) cases <- read.CaseFile(casefile, session=sess) memstream <- CaseMemoryStream(cases, session=sess) ## Should be the same as cases stopifnot(all.equal(MemoryStreamContents(memstream),cases)) MemoryStreamContents(memstream) <- cases CloseCaseStream(memstream) ## Don't forget to read off the value ## first if needed before closing. stopifnot(!isCaseStreamOpen(memstream)) ## This should return the cached value. MemoryStreamContents(memstream) ## Will clear stream when next open MemoryStreamContents(memstream) <- NULL OpenCaseStream(memstream) stopifnot(is.null(MemoryStreamContents(memstream))) CloseCaseStream(memstream) ## Second test, do this from scratch. casesabb <- data.frame(IDnum=1001:1010,NumCases=rep(1,10), A=c("A1","A1","A1","A1","A1","A2","A2","A2","A2","A2"), B1=c("B1","B1","B1","B2","B2","B2","B2","B2","B1","B1"), B2=c("B1","B1","B1","B1","B2","B2","B2","B2","B2","B1")) abbstream <- CaseMemoryStream(casesabb, session=sess) MemoryStreamContents(abbstream) CloseCaseStream(abbstream) abb <- CreateNetwork("ABB", session=sess) A <- NewDiscreteNode(abb,"A",c("A1","A2")) B1 <- NewDiscreteNode(abb,"B1",c("B1","B2")) B2 <- NewDiscreteNode(abb,"B2",c("B1","B2")) AddLink(A,B1) AddLink(A,B2) A[] <- c(.5,.5) NodeExperience(A) <- 10 B1["A1"] <- c(.8,.2) B1["A2"] <- c(.2,.8) B2["A1"] <- c(.8,.2) B2["A2"] <- c(.2,.8) NodeExperience(B1) <- c(10,10) NodeExperience(B2) <- c(10,10) abbstream <- CaseMemoryStream(casesabb, session=sess) ## This does not appear to work correctly abbstream <- ReadFindings(list(A,B1,B2),abbstream,"FIRST") NodeFinding(A) NodeFinding(B1) NodeFinding(B2) CloseCaseStream(abbstream) DeleteNetwork(abb) stopSession(sess)
Findings a set of values for each of the nodes in nodelist
such
that the probability of that value set is highest given the state of
any findings entered into the network. This is sometimes called the
“Most Probable Explanation” for the findings.
MostProbableConfig(net,nth = 0)
MostProbableConfig(net,nth = 0)
net |
An active and compiled |
nth |
Leave this at its default value of zero, it is for future expansion. |
The most probable configuration of the nodes in the Bayesian network
is the set of values for each of the nodes in the network which have
the highest joint probability. This may or may not be thee same as
setting the value of each node to the value that maximizes its
NodeBeliefs()
. Pearl (1988) describes a special
max-propagation algorithm which can calculate the most likely
configuration of nodes in a Bayesian network. This function runs that
algorithm. The probability that is maximized is the posterior
probability given the findings.
Note that this produces a configuration over all of the nodes in the
network, not just the nodes in some particular set. The Netica
documentation suggests running AbsorbNodes()
over the
unnecessary nodes first. Another possibility (if the set of
interesting nodes is small) is to call
JointProbability()
on the affected nodes and then
find the max of that.
A character vector whose names are the names of the nodes in the
network (see NetworkAllNodes(net)
) and whose values are
the names of the states that maximize the posterior probability given
the findings.
The documentation for the Netica function MostProbableConfig_bn()
states that likelihood findings (NodeLikelihood()
) are not
handled properly in MostProbableConfig()
. Seems to indicate
that this works properly, but some caution is still advised.
The Bayesian network literature also discusses algorithms for the 2nd,
3rd, 4th, etc. most likely findings. These algorithms are slightly
more difficult to implement, but are possible on future plans for the
Netica API, as it offers the nth
argument to the function
MostProbableConfig_bn(). At this point in time, it is an error
to set nth
to anything but 0.
Russell Almond
Pearl, J. (1988) Probabilistic Reasoning in Intelligent Systems: Networks of Plausible Inference. Morgan Kaufmann.
http://norsys.com/onLineAPIManual/index.html: MostProbableConfig_bn()
NeticaBN
, NodeBeliefs()
,
EnterNegativeFinding()
,
RetractNodeFinding()
, NodeFinding()
JointProbability()
,
FindingsProbability()
sess <- NeticaSession() startSession(sess) EMSMMotif <- ReadNetworks(system.file("sampleNets","EMSMMotif.dne", package="RNetica"), session=sess) CompileNetwork(EMSMMotif) obs <- NetworkNodesInSet(EMSMMotif,"Observable") prof <- NetworkNodesInSet(EMSMMotif,"Proficiency") NodeFinding(obs$Obs1a1) <- "Right" NodeFinding(obs$Obs1a2) <- "Wrong" NodeFinding(obs$Obs1b1) <- "Right" NodeFinding(obs$Obs1b2) <- "Wrong" mpe <- MostProbableConfig(EMSMMotif) ## Observed values should be set at their findings level. stopifnot ( mpe$Obs1a1 == "Right", mpe$Obs1a2 == "Wrong", mpe$Obs1b1 == "Right", mpe$Obs1b2 == "Wrong" ) ## MPE for just proficiency variables. mpe[names(prof)] DeleteNetwork(EMSMMotif) stopSession(sess)
sess <- NeticaSession() startSession(sess) EMSMMotif <- ReadNetworks(system.file("sampleNets","EMSMMotif.dne", package="RNetica"), session=sess) CompileNetwork(EMSMMotif) obs <- NetworkNodesInSet(EMSMMotif,"Observable") prof <- NetworkNodesInSet(EMSMMotif,"Proficiency") NodeFinding(obs$Obs1a1) <- "Right" NodeFinding(obs$Obs1a2) <- "Wrong" NodeFinding(obs$Obs1b1) <- "Right" NodeFinding(obs$Obs1b2) <- "Wrong" mpe <- MostProbableConfig(EMSMMotif) ## Observed values should be set at their findings level. stopifnot ( mpe$Obs1a1 == "Right", mpe$Obs1a2 == "Wrong", mpe$Obs1b1 == "Right", mpe$Obs1b2 == "Wrong" ) ## MPE for just proficiency variables. mpe[names(prof)] DeleteNetwork(EMSMMotif) stopSession(sess)
The mutual information is a measure of how closely related one node is
to another, i.e., how much information each node in nodelist
provides about the target
node. The expression
MutualInfo(target, nodelist)
calculates the mutual
information of each node in nodelist
with target
.
The function VarianceOfReal()
is similar, but instead it
measures the reduction in variance of the target. The target
node must be continuous or have numeric values assigned to all levels
using NodeLevels
.
MutualInfo(target, nodelist) VarianceOfReal(target, nodelist)
MutualInfo(target, nodelist) VarianceOfReal(target, nodelist)
target |
An active |
nodelist |
A non-empty list of active |
The mutual information between two discrete variables is defined as:
It is a measure of how much information X
provides about
Y
. This measure is appropriate when both X
and Y
are discrete variables.
Mutual information is often used to select the next best variable to test (in the educational context, this would be the next item to select for an adaptive test). The highest value of the mutual information will provide the most information. (See Chapter 7 of Almond et al, 2015).
The function VarianceOfReal(target,nodelist)
is
related, but in this case target
must either be continuous
(is.continuous(target)
is true) or have numeric
values assigned to each level using NodeLevels(target)
.
For this function, the value returned is the amount by which the
variance of target
is expected to be reduced if the value of
the observable node in the nodelist was learned. Again, higher values
indicate better information.
Returns a named numeric vector with the names corresponding to the
nodes in nodelist
and the value the mutual information or
variance reduction.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: MutualInfo_bn(), VarianceOfReal_bn()
Almond, R. G., Mislevy, R. J., Steinberg, L. S., Yan, D. & Williamson, D. M. (2015) Bayesian Networks in Educational Assessment. Springer.
is.continuous()
, NodeExpectedValue()
,
NodeLevels()
,
The function ewoe
returns the expected weight of
evidence which is a similar metric.
sess <- NeticaSession() startSession(sess) irt5 <- ReadNetworks(system.file("sampleNets","IRT5.dne", package="RNetica"), session=sess) irt5.theta <- NetworkFindNode(irt5,"Theta") irt5.x <- NetworkFindNode(irt5,paste("Item",1:5,sep="_")) CompileNetwork(irt5) MutualInfo(irt5.theta,irt5.x) VarianceOfReal(irt5.theta,irt5.x) DeleteNetwork(irt5) stopSession(sess)
sess <- NeticaSession() startSession(sess) irt5 <- ReadNetworks(system.file("sampleNets","IRT5.dne", package="RNetica"), session=sess) irt5.theta <- NetworkFindNode(irt5,"Theta") irt5.x <- NetworkFindNode(irt5,paste("Item",1:5,sep="_")) CompileNetwork(irt5) MutualInfo(irt5.theta,irt5.x) VarianceOfReal(irt5.theta,irt5.x) DeleteNetwork(irt5) stopSession(sess)
This file is now obsolete: See NeticaBN
for the
new class description.
This object is returned by various RNetica functions which create or
find network objects, and contain handles to the Bayesian network. A
NeticaBN
object represents an active network. The function
is.active()
tests whether the network is still loaded into
Netica's memory.
is.NeticaBN(x)
is.NeticaBN(x)
x |
The object to print or test |
This is an object of class NeticaBN
. It consists of a name,
and an invisible handle to a Netica network. The function
is.active()
tests the state of that handle and returns
FALSE
if the network is no longer in active memory (usually
because of a call to DeleteNetwork()
). The printed
representation depends on whether or not it is active (inactive nodes
print as "<Deleted Network: Name >"
).
For active networks, the equality test tests to see if both object point to the same object in Netica memory. Not that the name of the network is embedded in the object implementation and may get out of sync with the network, so the printed representations may be unequal even if it points to the same network. For inactive networks, the objects are compared using the cached names.
For toString()
a string. The function print()
is
usually called for its side effects.
The function is.NeticaBN()
returns a logical scalar depending
on whether or not its argument is a NeticaBN
.
The function Ops.NeticaBN()
returns a logical value depending on
whether the objects are equal.
NeticaBN
objects are all rendered inactive when
StopNetica()
is called, therefore they do not persist across R
sessions. Generally speaking, the network should be saved, using
WriteNetworks()
and then reloaded in the new session
using ReadNetworks()
. When a network is saved or loaded
the "Filename"
attribute is set, to provide a mechanism for
storing the filename across R sessions.
Russell Almond
http://norsys.com/onLineAPIurl/index.html: GetNetUserData_bn(), SetNetUserData_bn() (these are used to maintain the back pointers to the R object).
CreateNetwork()
,DeleteNetwork()
,
GetNamedNetworks()
,NetworkName()
,
is.active()
, NetworkAllNodes()
,
WriteNetworks()
, GetNetworkFileName()
,
## Not run: net1 <- CreateNetwork("aNet") stopifnot(is.NeticaBN(net1)) stopifnot(is.active(net1)) stopifnot(net1$Name=="aNet") net2 <- GetNamedNetworks("aNet") stopifnot(net2$Name=="aNet") stopifnot(net1==net2) NetworkName(net1) <- "Unused" stopifnot(net1==net2) netd <- DeleteNetwork(net1) stopifnot(!is.active(net1)) stopifnot(!is.active(net2)) stopifnot(netd$Name=="Unused") stopifnot(netd == net1) ## Warning: The following expression used to not be true (RNetica <0.5) ## but now is. net1 = net2 ## End(Not run)
## Not run: net1 <- CreateNetwork("aNet") stopifnot(is.NeticaBN(net1)) stopifnot(is.active(net1)) stopifnot(net1$Name=="aNet") net2 <- GetNamedNetworks("aNet") stopifnot(net2$Name=="aNet") stopifnot(net1==net2) NetworkName(net1) <- "Unused" stopifnot(net1==net2) netd <- DeleteNetwork(net1) stopifnot(!is.active(net1)) stopifnot(!is.active(net2)) stopifnot(netd$Name=="Unused") stopifnot(netd == net1) ## Warning: The following expression used to not be true (RNetica <0.5) ## but now is. net1 = net2 ## End(Not run)
"NeticaBN"
This is an R side container for a Netica netork. Note that it has a container for the nodes which have been advertised from the network.
A NeticaBN
is an R wrapper for the internal pointer to the
Netica nework. A network is said to be ‘active’ if it
references a network object in a current Netica session. A network
become inactive when it is deleted (with a call to
DeleteNetwork
) or when the R session is saved an
restored. In the latter case, if the network was saved with a call to
WriteNetworks
, then calling ReadNetworks
on the inactive network will reload it from the save file.
Generally, NeticaBN
objects are created with calls to either
CreateNetwork
or ReadNetworks
. Both
require a reference to an active NeticaSession
object. NeticaBN
objects are registered with the
NeticaSession
object, which contains a collection
of all of the networks known about by the session.
The nodes
field of the NeticaBN
object
(net$nodes
) contains a cache of all code
NeticaNode
objects that are contained by the
network and known about by R. Nodes are registered by their Netica
name so the expression net$nodes$nodename
or
net$nodes[[nodename]]
references a node with the name
nodename
in net
.
Note that R node objects are created when a node is created in R, but
not when a network is read in using ReadNetworks
. This
is useful for cases where the network is large and only a few nodes
will be reference in the R code. The function
NetworkFindNode()
will find a node by name and create
the R object corresponding to the node if needed. The function
NetworkAllNodes()
will create R objects for all nodes in
the net.
All reference classes extend and inherit methods from
"envRefClass"
. Note that because this is a
reference class unlike traditional S3 and S4 classes it can be
destructively modified. Also fields (slots) are accessed using the
‘$’ operator.
signature(e1 = "NeticaBN", e2 = "NeticaBN")
:
Tests for equality (mainly of pointers.
signature(x = "NeticaBN")
: Returns true if
the NeticaBN
objectcurrently references an active Netica
object, and returns false if it references a deleted network or a
network created in a previous sesion which has not be
re-activated.
signature(x = "NeticaBN")
: Creates a printed
representation.
signature(x = "NeticaBN")
: Creates a
character representation.
signature(el = "NeticaBN", set = "list")
:
Checks to see if el is in list of nets.
Note these should be regarded as read-only from user code.
Name
:Object of class character
giving the
Netica name of the network. Must follow the IDname
rules. This should not be set by user code, use
NetworkName
instead.
PathnameName
:Object of class character
giving
the path from which the network was last read or to which it was
last saved.
Netica_bn
:Object of class externalptr
linking
to the Netica object corresponding to this network.
Session
:Object of class NeticaSession
: a back
pointer to the NeticaSession
object in which
the network was created.
nodes
:Object of class environment
which
contains a cache of NeticaNode
objects
belonging to this network.
listNodes()
: Lists all of the cached nodes.
(Contrast this to NetworkAllNodes(net)
which lists
all nodes in the network.
searchNodes(pattern)
: Lists all cached nodes matching
the regular expression given in pattern. (See
objects
.)
show()
:Gives a detailed description of the object.
isActive()
:Returns true if the object currently points to a Netica network, and false if it does not.
findNode(nodename)
: Searches for a cached node with
name nodename
, returns it if found or NULL
if not.
clearErrors(severity)
: Calls clearErrors
on
the Session
object.
reportErrors(maxreport, clear, call)
: Calls
reportErrors
on the Session
object. Returns an
object of class NeticaCondition
if there was a
message, or NULL
if not.
signalErrors(maxreport, clear, call)
: Calls
signalErrors
on the Session
object. If there was a
problem, the appropriate condition is signaled, see
NeticaCondition
.
initialize(Name, Session, ...)
:Initialization function. Should not be called by user code.
deactivate()
:Destroys the pointer to the Netica object. Should not be called by user code.
deactivateNodes()
:Recursively deactives all nodes contained by this network. Should not be called by user code.
The NeticaBN
class was changed into a formal R6 reference class
as of version 0.5 of RNetica. Prior to that, it was an S3 class
created by adding attributes to a string. That proved to be less than
robust, as several R functions (notably c()
) would
strip the attributes.
Another change is the method for finding the network object from the
Netica pointer inside of the C code. Now the R objects are cached
inside of a NeticaSession
object by their netica
name. The R object is found by searching the cache inside of the
session object.
Russell Almond
http://norsys.com/onLineAPIurl/index.html: GetNetUserData_bn(), SetNetUserData_bn() (these are used to maintain the back pointers to the R object).
NeticaBN
objects are contained by
NeticaSession
objects and contain
NeticaNode
objects.
CreateNetwork()
,DeleteNetwork()
,
GetNamedNetworks()
,NetworkName()
,
is.active()
, NetworkAllNodes()
,
WriteNetworks()
, GetNetworkFileName()
,
sess <- NeticaSession() startSession(sess) net1 <- CreateNetwork("aNet",sess) stopifnot(is.NeticaBN(net1)) stopifnot(is.active(net1)) stopifnot(net1$Name=="aNet") net2 <- GetNamedNetworks("aNet",sess) stopifnot(net2$Name=="aNet") stopifnot(net1==net2) NetworkName(net1) <- "Unused" stopifnot(net1==net2) netd <- DeleteNetwork(net1) stopifnot(!is.active(net1)) stopifnot(!is.active(net2)) stopifnot(netd$Name=="Unused") stopifnot(netd == net1) stopSession(sess)
sess <- NeticaSession() startSession(sess) net1 <- CreateNetwork("aNet",sess) stopifnot(is.NeticaBN(net1)) stopifnot(is.active(net1)) stopifnot(net1$Name=="aNet") net2 <- GetNamedNetworks("aNet",sess) stopifnot(net2$Name=="aNet") stopifnot(net1==net2) NetworkName(net1) <- "Unused" stopifnot(net1==net2) netd <- DeleteNetwork(net1) stopifnot(!is.active(net1)) stopifnot(!is.active(net2)) stopifnot(netd$Name=="Unused") stopifnot(netd == net1) stopSession(sess)
The CaseStream
object is a wrapper around a
Netica stream which is used to read/write cases—sets of findings
entered into a Netica network. There are two subclasses:
FileCaseStream
and
MemoryCaseStream
. The function
ReadFindings
reads the findings from the stream and the
function WriteFindings
writes them out.
OpenCaseStream(oldstream) CloseCaseStream(stream) is.NeticaCaseStream(x) isCaseStreamOpen(stream) getCaseStreamPos(stream) getCaseStreamLastId(stream) getCaseStreamLastFreq(stream)
OpenCaseStream(oldstream) CloseCaseStream(stream) is.NeticaCaseStream(x) isCaseStreamOpen(stream) getCaseStreamPos(stream) getCaseStreamLastId(stream) getCaseStreamLastFreq(stream)
oldstream |
A previously closed |
stream |
A |
x |
A object to be printed or whose type is to be determined. |
A CaseStream
object is an R wrapper around
a Netica stream object. There are two special cases:
FileCaseStream
objects are streams focused on a
case file, and MemoryCaseStream
objects are
streams focused on a hunk of memory corresponding to an R data frame
object.
Although the function WriteFindings
always appends a new
case to the end of a file (and hence does not need to keep the stream
object open between calls), the function ReadFindings
will read (by default) sequentially from the cases in the stream, and
hence the stream needs to be kept open between calls.
The functions CaseFileStream
and
CaseMemoryStream
create new streams and open them.
The function OpenCaseStream
will reopen a previously closed
stream, and will issue a warning if the stream is already open.
The function CloseCaseStream
closes an open case stream (and is
harmless if the stream is already closed). Although RNetica tries to
close open case streams when they are garbage collected, users should
not count on this behavior and should close them manually. Also be
aware that all case streams are automatically closed when R is closes
or RNetica is unloaded. The function isCaseStreamOpen
tests to
see if the stream is open or closed. The function
WithOpenCaseStream
executes an arbitrary R expression in
a context where the stream is open, and then closed afterwards.
Netica internally keeps track of the current position of the stream
when it is read or written. The functions getCaseStreamPos
,
getCaseStreamLastId
and getCaseStreamLastFreq
get
information about the position in the file, the user generated id
number and the frequency/weight assigned to the case at the time the
stream was last read or written. In particular, the function
ReadFindings
returns a CaseStream
object, which should be queried to find the ID and Frequencies read
from the stream. When ReadFindings
reaches the end of
the stream, the value of getCaseStreamPos(stream)
will be
NA
.
The functions OpenCaseStream
and CloseCaseStream
both
return their argument, which should be a CaseStream
.
The function toString.CaseStream
returns a string providing
information about the source and status its argument.
The functions is.NeticaCaseStream
and isCaseStreamOpen
both
return logical values indicating whether or not the condition holds.
The latter function returns NA
if its argument is not a
CaseStream
.
The function getCaseStreamPos
returns a scalar integer values
giving the position of the last record read from or written to the
stream. The position is an integer corresponding to the number of
characters that have been read in the stream. If an attempt has been
made to read past the end of the stream, this value will be NA
.
The function getCaseStreamLastId
is a user specified integer
associated with the case last read from or written to the stream.
It's value is -1
if the user did not assign ID numbers.
The function getCaseStreamLastFreq
returns a numeric scalar
which is the weight associated with the last case read from or written
to stream
. If the user did not specify frequencies when the
stream was written, the value returned is -1
.
The functions LearnCPTs
and LearnCases
update the CPTs of a Bayesian network based on the cases in the case
stream.
The functions ReadNetworks
and
WriteNetworks
also use Netica streams
internally. However, as it is almost certainly a mistake to keep the
stream open after the network has been read or written, no
CaseStream
object is created.
Internally, a weak reference system is used to keep a list of Netica stream objects which need to be closed when RNetica is unloaded. Stream objects should also be forced closed when garbage collected. The weak reference system is somewhat experimental, so well designed code should manually close the streams when the program is through with them.
Stream objects are fragile, and will not survive saving and restoring
an R session. However, the object retains information about itself,
so that calling OpenCaseStream
on the saved object, should
reopen the stream. Note that any position information will be lost.
The functions LearnCPTs
and LearnCases
don't seem to work with MemoryCaseStream
s; for
now, work around by writing the data out to a file and then writing
using a FileCaseStream
.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: NewFileStream_ns(),NewMemoryStream_ns(), DeleteStream_ns() http://homepage.stat.uiowa.edu/~luke/R/references/weakfinex.html
See CaseStream
for details about the stream
object. See FileCaseStream
and
MemoryCaseStream
for specific details about these
stream types.
CaseFileDelimiter
, CaseFileMissingCode
,
WriteFindings
, ReadFindings
,
CaseMemoryStream
,CaseFileStream
,
WithOpenCaseStream
LearnCPTs
, LearnCases
sess <- NeticaSession() startSession(sess) abc <- CreateNetwork("ABC",sess) A <- NewDiscreteNode(abc,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc,"C",c("C1","C2")) AddLink(A,B) AddLink(A,C) AddLink(B,C) ## Outputfilename casefile <- tempfile("testcase",fileext=".cas") filestream <- CaseFileStream(casefile,sess) stopifnot(is.NeticaCaseStream(filestream), isCaseStreamOpen(filestream)) ## Case 1 NodeFinding(A) <- "A1" NodeFinding(B) <- "B1" NodeFinding(C) <- "C1" filestream <- WriteFindings(list(A,B,C),filestream,1001,1.0) stopifnot(getCaseStreamLastId(filestream)==1001, abs(getCaseStreamLastFreq(filestream)-1.0) <.0001) pos1 <- getCaseStreamPos(filestream) RetractNetFindings(abc) ## Case 2 NodeFinding(A) <- "A2" NodeFinding(B) <- "B2" NodeFinding(C) <- "C2" ## Double weight this case filestream <- WriteFindings(list(A,B,C),filestream,1002,2.0) pos2 <- getCaseStreamPos(filestream) stopifnot(pos2>pos1,getCaseStreamLastId(filestream)==1002, abs(getCaseStreamLastFreq(filestream)-2.0) <.0001) RetractNetFindings(abc) ## Case 3 NodeFinding(A) <- "A3" NodeFinding(B) <- "B3" ## C will be missing filestream <- WriteFindings(list(A,B,C),filestream,1003,1.0) stopifnot(getCaseStreamLastId(filestream)==1003, abs(getCaseStreamLastFreq(filestream)-1.0) <.0001) RetractNetFindings(abc) ## Close it filestream <- CloseCaseStream(filestream) stopifnot (is.NeticaCaseStream(filestream), !isCaseStreamOpen(filestream)) ## Reopen it filestream <- OpenCaseStream(filestream) stopifnot (is.NeticaCaseStream(filestream), isCaseStreamOpen(filestream)) ##Case 1 RetractNetFindings(abc) filestream <- ReadFindings(list(A,B,C),filestream,"FIRST") pos1a <- getCaseStreamPos(filestream) stopifnot(pos1a==pos1, getCaseStreamLastId(filestream)==1001, abs(getCaseStreamLastFreq(filestream)-1.0) <.0001) ##Case 2 RetractNetFindings(abc) filestream <- ReadFindings(list(A,B,C),filestream,"NEXT") stopifnot(getCaseStreamPos(filestream)==pos2, getCaseStreamLastId(filestream)==1002, abs(getCaseStreamLastFreq(filestream)-2.0) <.0001) ##Clean Up CloseCaseStream(filestream) CloseCaseStream(filestream) ## This should issue a warning but be ## harmless. DeleteNetwork(abc) stopSession(sess)
sess <- NeticaSession() startSession(sess) abc <- CreateNetwork("ABC",sess) A <- NewDiscreteNode(abc,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc,"C",c("C1","C2")) AddLink(A,B) AddLink(A,C) AddLink(B,C) ## Outputfilename casefile <- tempfile("testcase",fileext=".cas") filestream <- CaseFileStream(casefile,sess) stopifnot(is.NeticaCaseStream(filestream), isCaseStreamOpen(filestream)) ## Case 1 NodeFinding(A) <- "A1" NodeFinding(B) <- "B1" NodeFinding(C) <- "C1" filestream <- WriteFindings(list(A,B,C),filestream,1001,1.0) stopifnot(getCaseStreamLastId(filestream)==1001, abs(getCaseStreamLastFreq(filestream)-1.0) <.0001) pos1 <- getCaseStreamPos(filestream) RetractNetFindings(abc) ## Case 2 NodeFinding(A) <- "A2" NodeFinding(B) <- "B2" NodeFinding(C) <- "C2" ## Double weight this case filestream <- WriteFindings(list(A,B,C),filestream,1002,2.0) pos2 <- getCaseStreamPos(filestream) stopifnot(pos2>pos1,getCaseStreamLastId(filestream)==1002, abs(getCaseStreamLastFreq(filestream)-2.0) <.0001) RetractNetFindings(abc) ## Case 3 NodeFinding(A) <- "A3" NodeFinding(B) <- "B3" ## C will be missing filestream <- WriteFindings(list(A,B,C),filestream,1003,1.0) stopifnot(getCaseStreamLastId(filestream)==1003, abs(getCaseStreamLastFreq(filestream)-1.0) <.0001) RetractNetFindings(abc) ## Close it filestream <- CloseCaseStream(filestream) stopifnot (is.NeticaCaseStream(filestream), !isCaseStreamOpen(filestream)) ## Reopen it filestream <- OpenCaseStream(filestream) stopifnot (is.NeticaCaseStream(filestream), isCaseStreamOpen(filestream)) ##Case 1 RetractNetFindings(abc) filestream <- ReadFindings(list(A,B,C),filestream,"FIRST") pos1a <- getCaseStreamPos(filestream) stopifnot(pos1a==pos1, getCaseStreamLastId(filestream)==1001, abs(getCaseStreamLastFreq(filestream)-1.0) <.0001) ##Case 2 RetractNetFindings(abc) filestream <- ReadFindings(list(A,B,C),filestream,"NEXT") stopifnot(getCaseStreamPos(filestream)==pos2, getCaseStreamLastId(filestream)==1002, abs(getCaseStreamLastFreq(filestream)-2.0) <.0001) ##Clean Up CloseCaseStream(filestream) CloseCaseStream(filestream) ## This should issue a warning but be ## harmless. DeleteNetwork(abc) stopSession(sess)
OBSOLETE: See NeticaNode
(class) for current
(RNetica 0.5 and beyond) implementation.
This object is returned by various RNetica functions which create or
find nodes in a NeticaBN
network. A NeticaNode
object represents a node object inside of Netica's memory. The
function is.active()
tests whether the node is still a valid
reference.
is.NeticaNode(x)
is.NeticaNode(x)
x |
The object to print or test |
This information is current only for Version 0.4 and earlier of RNetica.
This is an object of class NeticaNode
. It consists of a name,
and an invisible handle to a Netica node. The function
is.active()
tests the state of that handle and returns
FALSE
if the node is no longer in active memory (usually
because of a call to DeleteNode()
or DeleteNetwork()
.
NeticaNode
s come in two types: discrete and continuous (see
is.discrete()
). The two types give slightly different
meanings to the NodeStates()
and
NodeLevels()
attributes of the node. The printed
representation shows whether the node is discrete, continuous or
inactive (deleted).
For active nodes, the equality test tests to see if both object point to the same object in Netica memory. Note that the name of the node is embedded in the R object implementation and may get out of sync with Netica memory, so the printed representations may be unequal even if it points to the same node. For inactive nodes, the objects are compared using the cached names.
For toString()
a string. The function print()
is
usually called for its side effects.
The function is.NeticaNode()
returns a logical scalar depending
on whether or not its argument is a NeticaBN
.
NeticaNode
objects are all rendered inactive when
StopNetica()
is called, therefore they do not persist across R
sessions. Generally speaking, the network should be saved, using
WriteNetworks()
and then reloaded in the new session
using ReadNetworks()
. The node objects should then be
recreated via a call to NetworkFindNode()
.
Note that RNetica is lazy about creating NeticaNode
objects for
nodes when a network is read from a file. Probably users should avoid
creating or saving NetworkNode
objects unless they are going to
use them frequently.
Russell Almond
http://norsys.com/onLurl/Manual/index.html: AddNodeToNodeset_bn(), RemoveNodeFromNodeset_bn(), IsNodeInNodeset_bn() GetNodeUserData_bn(), SetNodeUserData_bn() (these are used to maintain the back pointers to the R object).
NeticaBN
, NetworkFindNode()
,
is.active()
, is.discrete()
,
NewContinuousNode()
, NewDiscreteNode()
,
DeleteNodes()
, NodeName()
,
NodeStates()
, NodeLevels()
,
## Not run: nety <- CreateNetwork("yNode") node1 <- NewContinuousNode(nety,"aNode") stopifnot(is.NeticaNode(node1)) stopifnot(is.active(node1)) stopifnot(as.character(node1)=="aNode") node2 <- NetworkFindNode(nety,"aNode") stopifnot(as.character(node2)=="aNode") stopifnot(node1==node2) NodeName(node1) <- "Unused" stopifnot(node1==node2) ## Warning: The following expression is true! as.character(node1) != as.character(node2) noded <- DeleteNodes(node1) stopifnot(!is.active(node1)) stopifnot(!is.active(node2)) stopifnot(as.character(noded)=="Unused") stopifnot(noded == node1) ## Warning: The following expression is true! node1 != node2 DeleteNetwork(nety) ## End(Not run)
## Not run: nety <- CreateNetwork("yNode") node1 <- NewContinuousNode(nety,"aNode") stopifnot(is.NeticaNode(node1)) stopifnot(is.active(node1)) stopifnot(as.character(node1)=="aNode") node2 <- NetworkFindNode(nety,"aNode") stopifnot(as.character(node2)=="aNode") stopifnot(node1==node2) NodeName(node1) <- "Unused" stopifnot(node1==node2) ## Warning: The following expression is true! as.character(node1) != as.character(node2) noded <- DeleteNodes(node1) stopifnot(!is.active(node1)) stopifnot(!is.active(node2)) stopifnot(as.character(noded)=="Unused") stopifnot(noded == node1) ## Warning: The following expression is true! node1 != node2 DeleteNetwork(nety) ## End(Not run)
"NeticaNode"
This object is returned by various RNetica functions which create or
find nodes in a NeticaBN
network. A NeticaNode
object represents a node object inside of Netica's memory. The
function is.active()
tests whether the node is still a valid
reference.
This is an object of class NeticaNode
. It consists of a name,
and an pointer to a Netica node in the workspace. The function
is.active()
tests the state of that handle and returns
FALSE
if the node is no longer in active memory (usually
because of a call to DeleteNode()
or DeleteNetwork()
.
NeticaNode
s come in two types: discrete and continuous (see
is.discrete()
). The two types give slightly different
meanings to the NodeStates()
and
NodeLevels()
attributes of the node. The printed
representation shows whether the node is discrete, continuous or
inactive (deleted).
NeticaNode
objects are created at two different times. First,
when the user creates a node in a network using the
NewContinuousNode()
or NewDiscreteNode()
functions. The second is when a user first reads the network in from a
file using ReadNetworks
and then subsequently searches
for the node using NetworkFindNode
. Note that this
latter means that there may be nodes in the Netica network for which
no R object has yet been created. When NeticaNode
objects are
created, they are cached in the NeticaBN
object.
Cached objects can be referenced by the nodes
field of the
NeticaBN
object (which is an R
environment
). Thus, the expressions
net$nodes$nodename
and
net$nodes[[nodename]]
both reference a node with
the Netica name nodename
in the network
net
. Note that both of these expressions will yeild
NULL
if no R object has yet been created for the node. The
function NetworkAllNodes(net)
will as a side
effect create node objects for all of the nodes in net
.
The function match
(and consequently %in%
does not like it when the first argument is a node. To get around
this problem, wrap the node in a list. I've added a method for the
function is.element
which does this
automatically.
All reference classes extend and inherit methods from
"envRefClass"
. Note that because this is a
reference class unlike traditional S3 and S4 classes it can be
destructively modified. Also fields (slots) are accessed using the
‘$’ operator.
signature(x = "NeticaNode")
: Sets conditional
probabliity table for node, see Extract.NeticaNode.
signature(x = "NeticaNode")
: Gets conditional
probabliity table for node, see Extract.NeticaNode.
signature(x = "NeticaNode")
: Gets conditional
probabliity table for node, see Extract.NeticaNode.
signature(e1 = "NeticaNode", e2 = "ANY")
:
Tests two nodes for equality
signature(el = "NeticaNode", set = "list")
:
Checks to see if el is in list of nodes.
signature(x = "NeticaNode")
: Makes printed
representation.
signature(x = "NeticaNode")
: Makes character
representation.
Note these should be regarded as read-only from user code.
Name
:Object of class character
giving the
Netica name of the node. Must follow the IDname
rules. This should not be modified by user code, use
NodeName
instead.
Netica_Node
:Object of class externalptr
giving
the address of the node in Netica's memory space.
Net
:Object of class NeticaBN
, a
back reference to the network in which this node resides.
discrete
:Object of class logical
true if the
node is discrete and false otherwise.
show()
:Prints a description of the node.
isActive()
:Returns true if the object currently points to a Netica node, and false if it does not.
clearErrors(severity)
: Calls clearErrors
on
the Net$Session
object.
reportErrors(maxreport, clear, call)
: Calls
reportErrors
on the Net$Session
object. Returns an
object of class NeticaCondition
if there was a
message, or NULL
if not.
signalErrors(maxreport, clear, call)
: Calls
signalErrors
on the Net$Session
object. If there was a
problem, the appropriate condition is signaled, see
NeticaCondition
.
initialize(Name, Net, discrete, ...)
: Initialziation
function. Should not be called directly by user code. Use
NewDiscreteNode
or NewContinuousNode
instead.
deactivate()
:Recursively deactives all nodes contained by this network. Should not be called by user code.
NeticaNode
objects are all rendered inactive when
StopNetica()
is called, therefore they do not persist
across R sessions. Generally speaking, the network should be saved,
using WriteNetworks()
and then reloaded in the new
session using ReadNetworks()
. The node objects should
then be recreated via a call to NetworkFindNode()
or
NetworkAllNodes()
.
Russell Almond
http://norsys.com/onLurl/Manual/index.html: AddNodeToNodeset_bn(), RemoveNodeFromNodeset_bn(), IsNodeInNodeset_bn() GetNodeUserData_bn(), SetNodeUserData_bn() (these are used to maintain the back pointers to the R object).
Its container class can be found in NeticaBN
.
The help file Extract.NeticaNode
explains the principle
methods of referencing the conditional probability table.
NetworkFindNode()
,
is.active()
, is.discrete()
,
NewContinuousNode()
, NewDiscreteNode()
,
DeleteNodes()
, NodeName()
,
NodeStates()
, NodeLevels()
,
sess <- NeticaSession() startSession(sess) nety <- CreateNetwork("yNode",sess) node1 <- NewContinuousNode(nety,"aNode") stopifnot(is.NeticaNode(node1)) stopifnot(is.active(node1)) stopifnot(node1$Name=="aNode") node2 <- NetworkFindNode(nety,"aNode") stopifnot(node2$Name=="aNode") stopifnot(node1==node2) NodeName(node1) <- "Unused" stopifnot(node1==node2) node1$Name == node2$Name noded <- DeleteNodes(node1) stopifnot(!is.active(node1)) stopifnot(!is.active(node2)) stopifnot(noded$Name=="Unused") stopifnot(noded == node1) node1 == node2 DeleteNetwork(nety) stopSession(sess)
sess <- NeticaSession() startSession(sess) nety <- CreateNetwork("yNode",sess) node1 <- NewContinuousNode(nety,"aNode") stopifnot(is.NeticaNode(node1)) stopifnot(is.active(node1)) stopifnot(node1$Name=="aNode") node2 <- NetworkFindNode(nety,"aNode") stopifnot(node2$Name=="aNode") stopifnot(node1==node2) NodeName(node1) <- "Unused" stopifnot(node1==node2) node1$Name == node2$Name noded <- DeleteNodes(node1) stopifnot(!is.active(node1)) stopifnot(!is.active(node2)) stopifnot(noded$Name=="Unused") stopifnot(noded == node1) node1 == node2 DeleteNetwork(nety) stopSession(sess)
These functions create and manipulate Netica Random Number
Generators (NeticaRNG
. Note that the storage for
NeticaRNG objects should be freed when you are done with them by
calling FreeNeticaRNG(rng)
.
NewNeticaRNG(seed = sample.int(.Machine$integer.max,1L), session=getDefaultSession()) FreeNeticaRNG(rng) WithRNG(rng,expr) is.NeticaRNG(x) isNeticaRNGActive(rng)
NewNeticaRNG(seed = sample.int(.Machine$integer.max,1L), session=getDefaultSession()) FreeNeticaRNG(rng) WithRNG(rng,expr) is.NeticaRNG(x) isNeticaRNGActive(rng)
seed |
An unsigned integer to use as a seed for the random number generator. |
session |
An object of type |
rng |
A NeticaRNG object |
x |
An arbitrary object |
expr |
An expression to be executed. |
Netica supports random number generator objects which serve can be
used to generate random cases (GenerateRandomCase()
).
In either case explicitly creating a random number generator is
optional. If this is not done, the default random number generator is
uses, which is slightly slower because it needs to be threadsafe. As
RNetica probably adds more overhead than the non-threadsafe RNG, the
primary use for creating a NeticaRNG is to produce a reproducable
sequence of random cases.
Creating a random number generator with rng <-
NewNeticaRNG(seed)
generates an object in Netica space. The memory
for that object should be freed when that is complete. The expression
FreeNeticaRNG(rng)
frees this object. The function
WithRNG
can be used to execute code in a context where the RNG
will be freed after after completion or in the case of early
termination due to an error.
When the random number generator is freed, or if the R session or
Netica session is terminated, the NeticaRNG object will become
inactive. The function isNeticaRNGActive(rng)
tests to
see if the random number generator is active (the Netica version still
exists).
The value of NewNetiaRNG(seed)
is an active
NeticaRNG
object.
The value of FreeRNG(rng)
is its argument which is now
inactive.
The value of WithRNG(rng,expr)
is the result of evaluating
expr
.
The values of is.NeticaRNG(x)
and isNeticaRNGActive(rng)
are logical scalars.
There are two other uses of newly created Netica RNG objects in the
Netica Manual which are not currently supported by RNetica. One is to
simply generate uniform random numbers which seems superfluous given
R's richer random number generation facilities. The second it to
associate the RNG with a network. According to the manual, such RNGs
no longer need to be deleted when you are done with them. This seems
like it could lead to a situation where a single RNG was associated
with two networks and then the RNG was deleted when the first network
was deleted. Therefore the function NetworkSetRNG()
always creates a new RNG.
Internally, a weak reference system is used to keep a list of Netica RNG objects which need to be closed when RNetica is unloaded. RNG objects should also be forced closed when garbage collected. The weak reference system is somewhat experimental, so well designed code should manually close the RNG when the program is through with it.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: NewRandomGenerator_ns(), DeleteRandomGen_ns() http://homepage.stat.uiowa.edu/~luke/R/references/weakfinex.html
An object of NeticaRNG
which is what is produced
by a call to NewNeticaRNG
.
NetworkSetRNG()
, GenerateRandomCase()
sess <- NeticaSession() startSession(sess) rng <- NewNeticaRNG(123456789,sess) stopifnot(is.NeticaRNG(rng), isNeticaRNGActive(rng)) FreeNeticaRNG(rng) stopifnot(!isNeticaRNGActive(rng)) stopSession(sess)
sess <- NeticaSession() startSession(sess) rng <- NewNeticaRNG(123456789,sess) stopifnot(is.NeticaRNG(rng), isNeticaRNGActive(rng)) FreeNeticaRNG(rng) stopifnot(!isNeticaRNGActive(rng)) stopSession(sess)
"NeticaRNG"
This is an object containing a reference to a Netica Random Number
Generator. Note that the storage for NeticaRNG objects should be
freed when you are done with them by calling
FreeNeticaRNG(rng)
.
Netica supports random number generator objects which serve can be
used to generate random cases (GenerateRandomCase()
).
In either case explicitly creating a random number generator is
optional. If this is not done, the default random number generator is
uses, which is slightly slower because it needs to be threadsafe. As
RNetica probably adds more overhead than the non-threadsafe RNG, the
primary use for creating a NeticaRNG is to produce a reproducable
sequence of random cases.
Creating a random number generator with rng <-
NewNeticaRNG(seed)
generates an object in Netica space. The memory
for that object should be freed when that is complete. The expression
FreeNeticaRNG(rng)
frees this object. The function
WithRNG
can be used to execute code in a context where the RNG
will be freed after after completion or in the case of early
termination due to an error.
When the random number generator is freed, or if the R session or
Netica session is terminated, the NeticaRNG object will become
inactive. The function isNeticaRNGActive(rng)
tests to see if
the random number generator is active (the Netica version still
exists).
All reference classes extend and inherit methods from
"envRefClass"
. Note that because this is a
reference class unlike traditional S3 and S4 classes it can be
destructively modified. Also fields (slots) are accessed using the
‘$’ operator.
signature(x = "NeticaRNG")
: Produces a printer
representation of the RNG object.
signature(x = "NeticaRNG")
: Produces a string
representation of the RNG object.
Note these should be regarded as read-only from user code.
Name
:Object of class character
giving a name
to the RNG. These are autogenerated with a number.
Session
:Object of class NeticaSession
: a back
pointer to the NeticaSession
object in which
the network was created.
Netica_RNG
:Object of class externalptr
which
hold the pointer to the RNG object in the Netica workspace.
Seed
:Object of class integer
giving the seed
used to initialize the RNG.
show()
:Provides a printed description
free()
: Frees the RNG in Netica Memory. Equivalent
to FreeNeticaRNG
.
isActive()
:Test to see if the RNG is active (been created in Netica) or inactive (already freed).
clearErrors(severity)
: Calls clearErrors
on
the Session
object.
reportErrors(maxreport, clear, call)
: Calls
reportErrors
on the Session
object. Returns an
object of class NeticaCondition
if there was a
message, or NULL
if not.
signalErrors(maxreport, clear, call)
: Calls
signalErrors
on the Session
object. If there was a
problem, the appropriate condition is signaled, see
NeticaCondition
.
initialize(Name, Session, Seed, ...)
:Initialization function. Should not be called by user code.
There are two other uses of newly created Netica RNG objects in the
Netica Manual which are not currently supported by RNetica. One is to
simply generate uniform random numbers which seems superfluous given
R's richer random number generation facilities. The second it to
associate the RNG with a network. According to the manual, such RNGs
no longer need to be deleted when you are done with them. This seems
like it could lead to a situation where a single RNG was associated
with two networks and then the RNG was deleted when the first network
was deleted. Therefore the function NetworkSetRNG()
always creates a new RNG.
Internally, a weak reference system is used to keep a list of Netica RNG objects which need to be closed when RNetica is unloaded. RNG objects should also be forced closed when garbage collected. The weak reference system is somewhat experimental, so well designed code should manually close the RNG when the program is through with it.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: NewRandomGenerator_ns(), DeleteRandomGen_ns() http://homepage.stat.uiowa.edu/~luke/R/references/weakfinex.html
An object of NeticaRNG
which is produced
by a call to NewNeticaRNG
.
NetworkSetRNG()
, GenerateRandomCase()
sess <- NeticaSession() startSession(sess) rng <- NewNeticaRNG(123456789,sess) stopifnot(is.NeticaRNG(rng), isNeticaRNGActive(rng)) FreeNeticaRNG(rng) stopifnot(!isNeticaRNGActive(rng)) stopSession(sess)
sess <- NeticaSession() startSession(sess) rng <- NewNeticaRNG(123456789,sess) stopifnot(is.NeticaRNG(rng), isNeticaRNGActive(rng)) FreeNeticaRNG(rng) stopifnot(!isNeticaRNGActive(rng)) stopSession(sess)
This function creates a NeticaSession
object
which encapsulates the link between R and Netica. It also contains a
collection of the networks associated with this Netica session.
NeticaSession(...,LicenseKey=options("NeticaLicenseKey")[[1]], SessionName=paste("RNetica Session", date()), Checking=character(), maxmem=integer()) getDefaultSession()
NeticaSession(...,LicenseKey=options("NeticaLicenseKey")[[1]], SessionName=paste("RNetica Session", date()), Checking=character(), maxmem=integer()) getDefaultSession()
... |
Possible to pass other fields initializers for subclasses. Base class uses fields below. |
LicenseKey |
If supplied, this should be a character scalar providing the license key purchased from Norsys http://www.norsys.com/. If left as default, and if the license key is not set in Netica, RNetica will run in a limited mode. |
SessionName |
A character vector giving an identifier for the session. Only used in printing. |
Checking |
Object of class |
maxmem |
Object of class |
Starting with verison 0.5 of RNetica, in order to start Netica, you
must first create an object of class
NeticaSession
and then call
startSession
on that object. This object then contains
a pointer to the Netica environment, and networks are created within
the NeticaSession object.
Netica is commercial software. The RNetica package downloads and
installs the demonstration version of Netica which is limited in its
functionality (particularly in the size of the networks it handles).
Unlocking the full version of Netica requires a license key which can
be purchased from Norsys (http://www.Norsys.com/). They will
send a license key which unlocks the full capabilities of the shared
library. This should be given as the LicenseKey
argument to
the constructor. If no license key is applied, then Netica will run
in a limited mode which limits the number of networks and nodes in the
networks. This is sufficient to run the test cases, and explore the
capabilities, but for serious model building you will need to purchase
a license.
Starting with verion 0.9, RNetica looks for the license key in the
“NeticaLicenseKey” option, using the
options
function.
The checking
argument, if supplied, is used to call the Netica
function ArgumentChecking_ns()
. See the documentation of that
function for the meaning of the codes. The default value,
"REGULAR_CHECK"
is appropriate for most development situations.
The maxmem
argument, if supplied, is used to limit the amount
of memory used by Netica. This is passed in a call to the Netica
function LimitMemoryUsage_ns()
. Netica will complain if this
value is less than 200,000. Leaving this as NULL
will not
place limits on the size of Netica's memory for tables and things.
Prior to version 0.5, the Netica session pointer was managed inside of
the c layer of RNetica. Thus, the session was an implicit argument to
several functions. In particular, the functions
CreateNetwork
, GetNthNetwork
,
GetNamedNetworks
, and ReadNetworks
all now
have a session argument. A session argument is
also needed by some lower level functions which create Netica objects:
CaseFileDelimiter
, CaseFileMissingCode
,
CaseFileStream
, CaseMemoryStream
,
OpenCaseStream
and NewNeticaRNG
. For
backwards compatability, the default for the session argument is now
the value of getDefaultSession()
.
In the previous version the session was created an the
StartNetica()
function was called when the RNetica
namespace was attached. Thus the user did not need to worry about
starting the Netica session. To be backwards compatable, the function
getDefaultSession()
searches for a default NeticaSession object
and if necessary creates one and starts it.
The function getDefaultSession()
does the following steps:
It first looks for a variable DefaultNeticaSession
in
the global environment. If this exists, it will be used as the
session. If it does not exist, and R is running in
interactive mode, then the user will be prompted to
create one. If running in batch mode, or if the user does not want to
create the default environment, then getDefaultSession()
will
raise an error.
If creating a new session, it will look for a variable called
NeticaLicenseKey
in the global environment. If that exists,
it will be used as the license key when creating a new
session. If not, then a new limited session will be created.
If a session object was found in step 1, or created in step 2,
then, if necessary is it activated with a call to
startSession
.
An object of class NeticaSession
.
The Netica API is not free-as-in-speech software, the use of the Netica shared library makes you subject to the Netica License agreement (which can be found in the RNetica folder in your R library. If you do not agree to the terms of that license, please uninstall RNetica.
The Netica API is also not free-as-in-beer software. The demonstration version of the Netica API, however, is. In order for you to make full use of the RNetica API, you must purchase a Netica API license from Norsys (http://norsys.com/).
RNetica itself (the glue layers between R and Netica) is free (in both the speech and beer senses) software. Suggestions for improvements and bug fixes are welcome.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: NewNeticaEnviron_ns(), InitNetica2_bn(), CloseNetica_bn(), LimitMemoryUsage_ns(), ArgumentChecking_ns()
See NeticaSession
for a discription of methods
that use the Netica Session object.
startSession
, stopSession
,
CreateNetwork
, GetNthNetwork
,
GetNamedNetworks
, and ReadNetworks
. It is
## Not run: ## Create a fully licensed session, and save it as the default DefaultNeticaSession <- NeticaSession(LicenseKey="License Key from Norsys") ## Create a limited mode session sess <- NeticaSession() startSession(sess) NeticaVersion(sess) getDefaultSession() stopSession(sess) ## End(Not run)
## Not run: ## Create a fully licensed session, and save it as the default DefaultNeticaSession <- NeticaSession(LicenseKey="License Key from Norsys") ## Create a limited mode session sess <- NeticaSession() startSession(sess) NeticaVersion(sess) getDefaultSession() stopSession(sess) ## End(Not run)
"NeticaSession"
An R object which provides a link to the Netica session. One of these must be present and active to allow access to Netica from R. This object is also how one enables the Netica license.
A Netica session is an R wrapper for the internal pointer to the
Netica workspace. It is used by a number of high-level Netica
functions to provide access to the workspace, notably:
CreateNetwork
, GetNthNetwork
,
GetNamedNetworks
, and ReadNetworks
. It is
also needed by some lower level functions which create Netica objects:
CaseFileDelimiter
, CaseFileMissingCode
,
CaseFileStream
, CaseMemoryStream
,
OpenCaseStream
and NewNeticaRNG
.
When initially created, the NeticaSession object is not active; that
is, the connection to the internal Netica environment is not yet
established. Calling the method startSession
will start the
session, making it active. The method stopSession
will
deallocate the Netica workspace. Note that it will also make any
NeticaBN
or NeticaNode
objects it contains
inactive. The function is.active()
tests to see whether
the session is currently active.
Starting with the introduction of the NeticaSession class, a license
key purchased from Norsys (http://www.norsys.com/) is a field of
the NeticaSession class. This field should either be a character
scalar giving the complete string supplied by Norsys or a character
scalar vector of length zero (the default). The licence key is passed
to Netica with the call to startSession()
; if it is a valid
license key, then Netica starts in unlimited mode. If it is not a
valid license key, the Netica will start in a limited mode which
restricts the number of objects that can be created. All of the
examples in the documentation should work in the limited mode; but
people wanting to do serious work will need to purchase a license key.
As the LicenseKey is stored in the NeticaSession object, do not share dumps of the NeticaSession object with people who are not eligible to use your license.
The nets
field of the NeticaSession object contains a
collection (actually an environment) of all of the
networks. They are referenced using their Netica names. In
particular, the construct session$nets$netname
will return the
network named netname if it exists, or NULL
. Similarly, the
construct session$nets[[netname]]
will attempt to
return the network whose name is the value of netname
. The
method session$listNets()
lists the names of all networks
registered with the session. This collection is maintained by the
CreateNetwork
, DeleteNetwork
, and
ReadNetworks
, so it is almost certainly an error to try
and manually change it. Network objects should be renamed using
NetworkName
which will update the collection.
Note that if an R workspace containing a NeticaSession object is saved
and restored, then the nets
field will be a collection of
inactive NeticaBN
objects corresponding to the networks that
were open when the session was saved. As these contain the pathnames
where the networks were last saved, it can be used to reload the
networks for a project.
It is unknown what would happen if more than one NeticaSession is active at the same time. Many possibilities are less than ideal.
All reference classes extend and inherit methods from
"envRefClass"
. Note that because this is a
reference class unlike traditional S3 and S4 classes it can be
destructively modified. Also fields (slots) are accessed using the
‘$’ operator.
signature(x = "NeticaSession")
: Returns true
if the link to Netica is currently active and available and false if
not.
signature(session = "NeticaSession")
:
Starts the Netica session and makes it available.
signature(session = "NeticaSession")
:
Stops the Netica session and makes all NeticaBN
and
NeticaNode
objects inactive.
signature(session = "NeticaSession")
:
Stops and restarts the session.
The error reporting mechanism works through the session
object. The session$reportErrors()
and
session$clearErrors()
methods are used to maintain this
system. The NeticaBN
object contains a back pointer
to its session, and delegates error reporting to the session. In
particular, net$Session
returns the session object.
Similarly, a NeticaNode
contains a back pointer to the
NeticaBN
, so node$Net$Session
accesses the
session.
As of Version 0.10-1, the error reporting mechanism has been
updated. The reprotErrors
method has been changed to return
an object of type NeticaCondition
if Netica signaled
an error, warning or message during executing the API call. This is
of class “error”, “warning”, or “message”,
depending on severety of the message. If no message was returned,
then reportErrors
returns NULL
.
The recommended way to handle errors, used internally in most RNetica
functions, is to call the signalErrors
message. This will
throw the appropriate condition, calling stop
for errors,
warning
for warnings and
signalCondition
for conditions. This will cause
internal Netica errors to be handled with the same mechanisms used to
control ordinary R errors.
Note these should be regarded as read-only from user code.
LicenseKey
:Object of class character
giving
the license key obtained from Norsys
(http://www.norsys.com/. Leaving this field as
character(0)
will result in the limited version of Netica
being used.
SessionName
:Object of class character
giving
an identifier for the session. This is just used for printing.
NeticaHandle
:Object of class externalptr
giving the C memory location of the Netica API workspace. This
should not be manipulated by users. If the session is inactive,
this pointer will be nil.
Checking
:Object of class character
one of the
keywords:
"NO_CHECK"
, "QUICK_CHECK"
, "REGULAR_CHECK"
,
"COMPLETE_CHECK"
, or "QUERY_CHECK"
, which controls how
rigorous Netica is about checking errors. A value of character()
uses the Netica default which is "REGULAR_CHECK"
.
maxmem
:Object of class numeric
containing an
integer indicating the maximum amount of memory to be
used by the Netica shared library in bytes. If supplied, this
should be at least 200,000.
nets
:Object of class environment
used to store
NeticaBN
objects opened by this session. This should
be regarded as read-only by user code.
neticaVersion()
:Returns the netica version associated with this session.
show()
:Provides information about the session.
isActive()
:Returns a logical value indicating whether Netica is currently started.
listNets(pattern="")
:Lists the names of all of the networks registered with this session. If pattern is supplied it should be a regular expression, only nets whose name contains the regular expression will be listed.
initialize(..., SessionName, autostart)
: Creates a
new session. If autostart
is true, then the session will
be started after it is created.
findNet(netname)
: Returns a NeticaBN
object associated with a network, or NULL
if no network
with that name exists.
clearErrors(severity)
: Clears errors of a given
severity level or lower. Severity levels in order are:
"NOTHING_ERR"
, "REPORT_ERR"
,
"NOTICE_ERR"
, "WARNING_ERR"
, "ERROR_ERR"
,
"XXX_ERR"
. The default is to report all errors.
reportErrors(maxreport=9, clear = TRUE,
call= sys.call(sys.parent()))
: Reports errors. The
maxreport
value gives the maximum number of errors to
report. The clear
value (default true) asks if errors
should be cleared after reporting. If the internal Netica error
reporting function returns messages, then a new object of type
NeticaCondition
is created. The original Netica
errors are given in extra fields with the names “Fatal”,
“Error”, “Warning”, “Notice” and
“Report”. The default value for call is the function which
calls the session$reportErrors
method. If there are no
messages, NULL
is return instead of the condition.
signalErrors(maxreport, clear, call)
: Calls
reportErrors
, and then if the return result is an
“error”, calls stop
; if the result is a
“warning”, calls warning
; if the reseult is a
“message”, calls signalCondition
(which
by default does nothing). If reportErrors
returns
NULL
it does nothing. If the signaling the condition does
not result in a non-local exit to the function, it returns the
output of reportErrors
invisibly.
The session object is part of a fundamental redesign of the guts of the way RNetica works starting with version 0.5. There are three features of this redesign:
The old NeticaBN
and NeticaNode
classes, which were implemented as S3 classes formed by adding
attributes to strings, have been replaced with R6 reference classes,
which should be more robust. In particular, the
c()
command strips attributes, which tended to
destroy node and net objects.
The session pointer used to be handled with a global variable in the RNetica source code. It is now a field in the new session object. This should allow more flexibility as well as not relying on a hidden mechanism.
Instead of using back-pointers from the Netica objects, Sessions contain an environment where nets are registered and nets contain an environment where nodes are registered. This should fix a problem with the backpointers pointing to the wrong location.
In the earlier design, the session object was hidden. For that
reason, the function getDefaultSession()
has been
added. This looks for an object called DefaultNeticaSession
in the gobal environment. If this object does not exist, the user
will be prompted to make one the first time
getDefaultSession()
is called. This is the default
for most high level functions which take a section argument;
hopefully, this will provide backwards compatability.
Russell Almond
https://pluto.coe.fsu.edu/RNetica/
CreateNetwork
, GetNthNetwork
,
GetNamedNetworks
, and ReadNetworks
. It is
also needed by some lower level functions which create Netica objects:
CaseFileDelimiter
, CaseFileMissingCode
,
CaseFileStream
, CaseMemoryStream
,
OpenCaseStream
and NewNeticaRNG
.
## Create a limited mode session ## Not run: ## Create a fully licensed session, and save it as the default DefaultNeticaSession <- NeticaSession(LicenseKey="License Key from Norsys") ## End(Not run) sess <- NeticaSession() startSession(sess) NeticaVersion(sess) myNet <- CreateNetwork("myNet",sess) stopifnot(myNet==sess$nets$myNet) stopifnot(myNet==sess$nets[["myNet"]]) stopifnot(myNet==sess$findNet("myNet")) stopifnot(identical(sess$listNets(),c("myNet"))) sess$reportErrors() sess$clearErrors() ## Not necessary as the previous statement clears too. stopSession(sess) ## Not run: ## Shows how to restore networks from a default session ## Existing in the workspace for (netname in DefaultNeticaSession$listNets()) { net <- DefaultNeticaSession$findNet(netname) ReadNetworks(GetNetworkFileName(net),DefaultNeticaSession) } ## End(Not run)
## Create a limited mode session ## Not run: ## Create a fully licensed session, and save it as the default DefaultNeticaSession <- NeticaSession(LicenseKey="License Key from Norsys") ## End(Not run) sess <- NeticaSession() startSession(sess) NeticaVersion(sess) myNet <- CreateNetwork("myNet",sess) stopifnot(myNet==sess$nets$myNet) stopifnot(myNet==sess$nets[["myNet"]]) stopifnot(myNet==sess$findNet("myNet")) stopifnot(identical(sess$listNets(),c("myNet"))) sess$reportErrors() sess$clearErrors() ## Not necessary as the previous statement clears too. stopSession(sess) ## Not run: ## Shows how to restore networks from a default session ## Existing in the workspace for (netname in DefaultNeticaSession$listNets()) { net <- DefaultNeticaSession$findNet(netname) ReadNetworks(GetNetworkFileName(net),DefaultNeticaSession) } ## End(Not run)
The version number of Netica is returned as both an integer and a string.
NeticaVersion(session=getDefaultSession())
NeticaVersion(session=getDefaultSession())
session |
An object of type |
This is a synnonym for session$neticaVersion()
(see
NeticaSession
).
This must be called after the call to StartNetica()
.
A list with two elements:
number |
Netica version number times 100 (to make it an integer). |
message |
String defining Netica version. |
RNetica was developed with Netica API 5.04
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNeticaVersion_bn()
sess <- NeticaSession() startSession(sess) print(sess$neticaVersion()$message) stopifnot(NeticaVersion(sess)$number > 409) ## Version 4.09 is a popular one. stopSession(sess)
sess <- NeticaSession() startSession(sess) print(sess$neticaVersion()$message) stopifnot(NeticaVersion(sess)$number > 409) ## Version 4.09 is a popular one. stopSession(sess)
The function NetworkFindNode
finds a node in a
NeticaBN
with the given name. If no node with the
specified name found, it will return NULL
. The function
NetworkAllNodes()
returns a list of all nodes in the network.
NetworkFindNode(net, name) NetworkAllNodes(net)
NetworkFindNode(net, name) NetworkAllNodes(net)
net |
The |
name |
A character vector giving the name or names of the desired nodes.
Names must follow the |
Although each NeticaNode
belongs to a single network, a
network contains many nodes. Within a network, a node is uniquely
identified by its name. However, nodes can be renamed (see
NodeName()
).
The function NetworkAllNodes()
returns all the nodes in the
network, however, the order of the nodes in the network could be
different in different calls to this function.
Starting with RNetica version 0.5, NeticaBN
objects keep a cache of node objects in the environment
net$nodes
. In particular, the methods
net$findNode()
will search the cache, and
net$listNodes()
will list the names of the nodes in the
cache. Also, net$nodes$nodename
or
net$nodes[["nodename"]]
will fetch the cached node
(if it exists) or return NULL
if it does not.
Nodes that are created in RNetica, using NewDiscreteNode
or NewContinuousNode
are automatically added to the
cache. This is also true of other functions which return
NeticaNode
objects. For example,
NodeParents(node)
will add the parents of
node to the cache if they are not there already.
A potential problem arises when the network is read from a file using
ReadNetworks
. This function does not automatically
cache the nodes. Calling NetworkFindNode
will add the nodes to
the cache. Calling NetworkAllNodes
will add all nodes to the
cache. Calling NetworkNodesInSet
can be used to pull
just a subsetof nodes into the cache.
The NeticaNode
object or list of NeticaNode
objects corresponding to names
, or a list of all node objects for
NetworkAllNodes()
. In the latter case, the ‘names’
attribute of the returned list will be set
to the node names.
NeticaNode
objects do not survive the life of a
Netica session (or by implication an R session). So the safest way to
"save" a NeticaNode
object is to recreate it using
NetworkFindNode()
after the network is reloaded.
Russell Almond
http://norsys.com/onLineAPIManual/index.html, GetNodeNamed_bn(), GetNetNodes_bn()
NeticaBN
talks more about the node cache and has
other functions for manipulating it.
NetworkNodesInSet
can be used to find a labeled subset
of nodes.
NodeNet()
retrieves the network from the node.
sess <- NeticaSession() startSession(sess) tnet <- CreateNetwork("TestNet", session=sess) nodes <- NewDiscreteNode(tnet,c("A","B","C")) nodeA <- NetworkFindNode(tnet,"A") stopifnot (nodeA==nodes[[1]]) nodeBC <- NetworkFindNode(tnet,c("B","C")) stopifnot(nodeBC[[1]]==nodes[[2]]) stopifnot(nodeBC[[2]]==nodes[[3]]) allnodes <- NetworkAllNodes(tnet) stopifnot(length(allnodes)==3) stopifnot(is.element(nodeA,allnodes)) ## NodeA in there somewhere. ## Not run: ## Safe way to preserve node and network objects across R sessions. tnet <- WriteNetworks(tnet,"Tnet.neta") q(save="yes") # R library(RNetica) sess <- NeticaSession() startSession(sess) tnet <- ReadNetworks(tnet, session=sess) nodes <- NetworkFindNodes(tnet,tnet$listNodes()) ## End(Not run) DeleteNetwork(tnet) stopSession(sess)
sess <- NeticaSession() startSession(sess) tnet <- CreateNetwork("TestNet", session=sess) nodes <- NewDiscreteNode(tnet,c("A","B","C")) nodeA <- NetworkFindNode(tnet,"A") stopifnot (nodeA==nodes[[1]]) nodeBC <- NetworkFindNode(tnet,c("B","C")) stopifnot(nodeBC[[1]]==nodes[[2]]) stopifnot(nodeBC[[2]]==nodes[[3]]) allnodes <- NetworkAllNodes(tnet) stopifnot(length(allnodes)==3) stopifnot(is.element(nodeA,allnodes)) ## NodeA in there somewhere. ## Not run: ## Safe way to preserve node and network objects across R sessions. tnet <- WriteNetworks(tnet,"Tnet.neta") q(save="yes") # R library(RNetica) sess <- NeticaSession() startSession(sess) tnet <- ReadNetworks(tnet, session=sess) nodes <- NetworkFindNodes(tnet,tnet$listNodes()) ## End(Not run) DeleteNetwork(tnet) stopSession(sess)
When a link is detached through setting a NodeParents()
to NULL
, or through copying a node but not its parent to a new
network, this leaves a stub node, an unsatisfied connection.
This function runs through the set of nodes in a network and lists the
names of all unsatisfied connections.
NetworkFootprint(net)
NetworkFootprint(net)
net |
An active |
Stub nodes – unsatisfied links or connections – can happen in two
ways. Either by setting one of the values of
NodeParents(node)
to NULL
, or by copying a node
(using CopyNodes()
) without copying its parents. (This
can also be done in the Netica GUI by detaching the link from the
parent end). This this case Netica names the
NodeInputNames()
according to the name of the old node.
The function NetworkFootprint(net)
search all of the nodes in
net
to find stub nodes, and reports the
NodeInputNames()
of the stub nodes. This function provides a
test for unsatisfied connections, and should be of assistance when
joining two networks together. The function
AdjoinNetwork(sm,em)
joins two networks together and
attempts to resolve the unsatisfied connections in em
.
One particular application of the footprint is in the EM–SM algorithm
(Almond et al, 1999; Almond and Mislevy, 1999). Here it is assumed
that nodes in the footprint of an evidence model will be joined.
Making a clique node MakeCliqueNode()
ensures that joint
information from the evidence model will find a good home in the
system model network.
A character vector giving the input names of the stub nodes in
net
. Duplicate values are removed.
Russell Almond
Almond, R. G. & Mislevy, R. J. (1999) Graphical models and computerized adaptive testing. Applied Psychological Measurement, 23, 223-238.
Almond, R., Herskovits, E., Mislevy, R. J., & Steinberg, L. S. (1999). Transfer of information between system and evidence models. In Artificial Intelligence and Statistics 99, Proceedings (pp. 181–186). Morgan-Kaufman
NeticaNode
, NodeParents()
,
MakeCliqueNode()
, NodeInputNames()
,
CopyNodes()
,AdjoinNetwork()
sess <- NeticaSession() startSession(sess) ## System/Student model EMSMSystem <- ReadNetworks(system.file("sampleNets","System.dne", package="RNetica"), session=sess) CompileNetwork(EMSMSystem) JunctionTreeReport(EMSMSystem) ## Evidence model for Task 1a EMTask1a <- ReadNetworks(system.file("sampleNets","EMTask1a.dne", package="RNetica"), session=sess) NetworkFootprint(EMTask1a) ## The corresponding clique is not in system model, so force it in. MakeCliqueNode(NetworkFindNode(EMSMSystem, NetworkFootprint(EMTask1a))) CompileNetwork(EMSMSystem) JunctionTreeReport(EMSMSystem) ## Evidence model for Task 2a EMTask2a <- ReadNetworks(system.file("sampleNets","EMTask2a.dne", package="RNetica"), session=sess) NetworkFootprint(EMTask2a) ## This is already a clique, so nothing to do. DeleteNetwork(list(EMSMSystem,EMTask1a,EMTask2a)) stopSession(sess)
sess <- NeticaSession() startSession(sess) ## System/Student model EMSMSystem <- ReadNetworks(system.file("sampleNets","System.dne", package="RNetica"), session=sess) CompileNetwork(EMSMSystem) JunctionTreeReport(EMSMSystem) ## Evidence model for Task 1a EMTask1a <- ReadNetworks(system.file("sampleNets","EMTask1a.dne", package="RNetica"), session=sess) NetworkFootprint(EMTask1a) ## The corresponding clique is not in system model, so force it in. MakeCliqueNode(NetworkFindNode(EMSMSystem, NetworkFootprint(EMTask1a))) CompileNetwork(EMSMSystem) JunctionTreeReport(EMSMSystem) ## Evidence model for Task 2a EMTask2a <- ReadNetworks(system.file("sampleNets","EMTask2a.dne", package="RNetica"), session=sess) NetworkFootprint(EMTask2a) ## This is already a clique, so nothing to do. DeleteNetwork(list(EMSMSystem,EMTask1a,EMTask2a)) stopSession(sess)
Gets or sets the name of the network. Names must conform to the
IDname
rules.
NetworkName(net, internal=FALSE) NetworkName(net) <- value
NetworkName(net, internal=FALSE) NetworkName(net) <- value
net |
A |
internal |
A logical scalar. If true, the actual Netica object will be consulted, if false, a cached value in the R object will be used. |
value |
A character scalar containing the new name. |
Network names must conform to the IDname
rules for
Netica identifiers. Trying to set the network to a name that does not
conform to the rules will produce an error, as will trying to set the
network name to a name that corresponds to another different network.
The NetworkTitle()
function provides another way to name
a network which is not subject to the IDname
restrictions.
Note that the name of the network is stored in two places: in the
Name
field of the NeticaBN
object
(net$Name
), and internally in the Netica object. These
should be the same; however, may not be. The internal
field is
used to force a check of the internal Netica object rather than the
field in the R object.
The name of the network as a character vector of length 1.
The setter method returns the modified object.
This paragraph is obsolete as of RNetica version 0.5, it describes the previous versions only.
NeticaBN
objects are internally implemented as character vectors
giving the name of the network. If a network is renamed, then it is
possible that R will hold onto an old reference that still using the
old name. In this case, NetworkName(net)
will give the correct
name, and GetNamedNets(NetworkName(net))
will return a
reference to a corrected object.
Starting with RNetica 0.5, NeticaBN
objects are
cached in the NeticaSession
object. The setter
method for NetworkName
updates the cache as well.
In versions of RNetica less than 0.5, trying to set the name of a node to a name that was already used would generate a warning instead of an error. It now generates an error.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNetName_bn(), SetNetName_bn()
CreateNetwork()
, NeticaBN
,
GetNamedNetworks()
, NetworkTitle()
sess <- NeticaSession() startSession(sess) net <- CreateNetwork("funNet", session=sess) netcached <- net stopifnot(!is.null(sess$findNet("funNet"))) stopifnot(NetworkName(net)=="funNet") stopifnot(NetworkName(net,internal=TRUE)=="funNet") NetworkName(net)<-"SomethingElse" stopifnot(net$Name=="SomethingElse") stopifnot(is.null(sess$findNet("funNet"))) stopifnot(!is.null(sess$findNet("SomethingElse"))) stopifnot(NetworkName(net)==NetworkName(netcached)) stopifnot(NetworkName(net)==NetworkName(netcached,internal=TRUE)) net1 <- CreateNetwork("funnyNet", session=sess) cat("Next statement should generate an error message.\n") nn <- try(NetworkName(net1) <- "SomethingElse") stopifnot(is(nn,"try-error")) DeleteNetwork(net) DeleteNetwork(net1) stopSession(sess)
sess <- NeticaSession() startSession(sess) net <- CreateNetwork("funNet", session=sess) netcached <- net stopifnot(!is.null(sess$findNet("funNet"))) stopifnot(NetworkName(net)=="funNet") stopifnot(NetworkName(net,internal=TRUE)=="funNet") NetworkName(net)<-"SomethingElse" stopifnot(net$Name=="SomethingElse") stopifnot(is.null(sess$findNet("funNet"))) stopifnot(!is.null(sess$findNet("SomethingElse"))) stopifnot(NetworkName(net)==NetworkName(netcached)) stopifnot(NetworkName(net)==NetworkName(netcached,internal=TRUE)) net1 <- CreateNetwork("funnyNet", session=sess) cat("Next statement should generate an error message.\n") nn <- try(NetworkName(net1) <- "SomethingElse") stopifnot(is(nn,"try-error")) DeleteNetwork(net) DeleteNetwork(net1) stopSession(sess)
Returns the display colour associated with a node set or sets the node
set colour to a specified value. The colour of the node in the Netica
GUI will be the colour of the highest priority node set associated with
the node (see NetworkSetPriority()
.
NetworkNodeSetColor(net, setname, newcolor)
NetworkNodeSetColor(net, setname, newcolor)
net |
An active |
setname |
A character scalar giving the name of the node set to be coloured. |
newcolor |
An optional scalar of any of the three kind of R colours, i.e., either a
colour name (an element of |
Netica determines the visual style of a node by stepping through the
node sets to which the node belongs in priority order (see
NetworkSetPriority()
) . Each node set
can either have a colour set, or a flag set to indicate that the next
node in order or priority should be used to determine the appearance
of the node. The expression NetworkNodeSetColor(net,setname,NA)
sets the flag so that membership in setname
does not affect the
display of the node.
The function
NetworkNodeSetColor(net,setname,colour)
sets
the colour associated with the visual display of these nodes (this is
only visible when the network is open in the Netica GUI). The colour
can be specified in any of the usual ways that colours are specified
in R (see col2rgb()
). The special value
NA
is used to indicate that the set should be
‘transparent’, that is the colour of the next set in priority
should be used to colour the node.
The function NetworkNodeSetColor(net,setname)
, with
the third argument missing, returns the current node set colour instead of
setting it.
The old value of the node colour as hexadecimal string value of the
form "#rrggbb"
.
The colors of the built-in Netica node sets serve as the ultimate default for the display of nodes. These cannot be set or queried through this function. (This is a limitation of the Netica API).
Russell Almond
http://norsys.com/onLurl/Manual/index.html: ReorderNodesets_bn(), SetNodesetColor_bn()
NeticaNode
, NodeSets()
,
NetworkNodeSets()
, col2rgb()
,
rgb()
,
NetworkNodesInSet()
, NetworkSetPriority()
sess <- NeticaSession() startSession(sess) nsnet <- CreateNetwork("NodeSetExample", session=sess) Ability <- NewContinuousNode(nsnet,"Ability") X1 <- NewDiscreteNode(nsnet,"Item1",c("Right","Wrong")) EssayScore <- NewDiscreteNode(nsnet,"EssayScore",paste("level",5:0,sep="_")) Value <- NewContinuousNode(nsnet,"Value") NodeKind(Value) <- "Utility" Placement <- NewDiscreteNode(nsnet,"Placement", c("Advanced","Regular","Remedial")) NodeKind(Placement) <- "Decision" NodeSets(Ability) <- "ReportingVariable" NodeSets(X1) <- "Observable" NodeSets(EssayScore) <- c("ReportingVariable","Observable") ## Default colour is NA (transparent) stopifnot( is.na(NetworkNodeSetColor(nsnet,"Observable")) ) ## Make Reporting variables a pale blue NetworkNodeSetColor(nsnet,"ReportingVariable",rgb(1,.4,.4)) stopifnot( NetworkNodeSetColor(nsnet,"ReportingVariable") == "#ff6666" ) ## Using R (nee X11) color list. NetworkNodeSetColor(nsnet,"Observable","wheat2") stopifnot( NetworkNodeSetColor(nsnet,"ReportingVariable") == "#ff6666" ) DeleteNetwork(nsnet) stopSession(sess)
sess <- NeticaSession() startSession(sess) nsnet <- CreateNetwork("NodeSetExample", session=sess) Ability <- NewContinuousNode(nsnet,"Ability") X1 <- NewDiscreteNode(nsnet,"Item1",c("Right","Wrong")) EssayScore <- NewDiscreteNode(nsnet,"EssayScore",paste("level",5:0,sep="_")) Value <- NewContinuousNode(nsnet,"Value") NodeKind(Value) <- "Utility" Placement <- NewDiscreteNode(nsnet,"Placement", c("Advanced","Regular","Remedial")) NodeKind(Placement) <- "Decision" NodeSets(Ability) <- "ReportingVariable" NodeSets(X1) <- "Observable" NodeSets(EssayScore) <- c("ReportingVariable","Observable") ## Default colour is NA (transparent) stopifnot( is.na(NetworkNodeSetColor(nsnet,"Observable")) ) ## Make Reporting variables a pale blue NetworkNodeSetColor(nsnet,"ReportingVariable",rgb(1,.4,.4)) stopifnot( NetworkNodeSetColor(nsnet,"ReportingVariable") == "#ff6666" ) ## Using R (nee X11) color list. NetworkNodeSetColor(nsnet,"Observable","wheat2") stopifnot( NetworkNodeSetColor(nsnet,"ReportingVariable") == "#ff6666" ) DeleteNetwork(nsnet) stopSession(sess)
A node set is a character label associated with a node which provides information about its role in the models. This function returns the complete list of node sets associated with any node in the network.
NetworkNodeSets(net, incSystem = FALSE)
NetworkNodeSets(net, incSystem = FALSE)
net |
An active |
incSystem |
A logical flag. If |
Netica node sets are a collection of string labels that can be
associated with various nodes in a network using the function
NodeSets()
. Node sets do not have any meaning to
Netica: node set membership only affect the way the node is displayed
(see NetworkNodeSetColor()
). One purpose of node sets
is to label a set of nodes that play a similar role in the model. For
example, "ReportingVariable"
or "Observable"
.
The expression NetworkNodeSets(net)
returns the node
sets that are currently associated with any node in net. If
incSystem=TRUE
, then the internal Netica system node sets will
be included as well. These begin with a colon (‘:’). This
value cannot be set directly, only indirectly through the use of
NodeSets
.
A character vector giving the node sets used by the network.
Node sets cannot be destroyed, only created. An empty node set has no effect.
Russell Almond
http://norsys.com/onLurl/Manual/index.html: GetAllNodesets_bn()
NeticaNode
, NodeSets()
,
NetworkSetPriority()
,
NetworkNodesInSet()
, NetworkNodeSetColor()
sess <- NeticaSession() startSession(sess) nsnet <- CreateNetwork("NodeSetExample", session=sess) Ability <- NewContinuousNode(nsnet,"Ability") EssayScore <- NewDiscreteNode(nsnet,"EssayScore",paste("level",5:0,sep="_")) Value <- NewContinuousNode(nsnet,"Value") NodeKind(Value) <- "Utility" Placement <- NewDiscreteNode(nsnet,"Placement", c("Advanced","Regular","Remedial")) NodeKind(Placement) <- "Decision" stopifnot( length(NetworkNodeSets(nsnet)) == 0, ## Nothing set yet length(NetworkNodeSets(nsnet,TRUE)) == 22 ## Number of system states ) NodeSets(Ability) <- "ReportingVariable" stopifnot( NetworkNodeSets(nsnet) == "ReportingVariable" ) NodeSets(EssayScore) <- "Observable" stopifnot( setequal(NetworkNodeSets(nsnet),c("Observable","ReportingVariable")) ) ## Changing spelling of name adds new set, doesn't delete the old one. NodeSets(EssayScore) <- "Observables" stopifnot( setequal(NetworkNodeSets(nsnet), c("Observables", "Observable","ReportingVariable")) ) ## Nor does deletion NodeSets(Ability) <- character() stopifnot( setequal(NetworkNodeSets(nsnet), c("Observables", "Observable","ReportingVariable")) ) DeleteNetwork(nsnet) stopSession(sess)
sess <- NeticaSession() startSession(sess) nsnet <- CreateNetwork("NodeSetExample", session=sess) Ability <- NewContinuousNode(nsnet,"Ability") EssayScore <- NewDiscreteNode(nsnet,"EssayScore",paste("level",5:0,sep="_")) Value <- NewContinuousNode(nsnet,"Value") NodeKind(Value) <- "Utility" Placement <- NewDiscreteNode(nsnet,"Placement", c("Advanced","Regular","Remedial")) NodeKind(Placement) <- "Decision" stopifnot( length(NetworkNodeSets(nsnet)) == 0, ## Nothing set yet length(NetworkNodeSets(nsnet,TRUE)) == 22 ## Number of system states ) NodeSets(Ability) <- "ReportingVariable" stopifnot( NetworkNodeSets(nsnet) == "ReportingVariable" ) NodeSets(EssayScore) <- "Observable" stopifnot( setequal(NetworkNodeSets(nsnet),c("Observable","ReportingVariable")) ) ## Changing spelling of name adds new set, doesn't delete the old one. NodeSets(EssayScore) <- "Observables" stopifnot( setequal(NetworkNodeSets(nsnet), c("Observables", "Observable","ReportingVariable")) ) ## Nor does deletion NodeSets(Ability) <- character() stopifnot( setequal(NetworkNodeSets(nsnet), c("Observables", "Observable","ReportingVariable")) ) DeleteNetwork(nsnet) stopSession(sess)
A node set is a character label associated with a node which provides information about its role in the models. This function returns a list of all nodes labeled with a particular node set.
NetworkNodesInSet(net, setname) NetworkNodesInSet(net, setname) <- value
NetworkNodesInSet(net, setname) NetworkNodesInSet(net, setname) <- value
net |
An active |
setname |
A character scalar giving the node set to look for. |
value |
A list of active |
Netica node sets are a collection of string labels that can be
associated with various nodes in a network using the function
NodeSets()
. Node sets do not have any meaning to
Netica: node set membership only affect the way the node is displayed
(see NetworkNodeSetColor()
). One purpose of node sets
is to label a set of nodes that play a similar role in the model. For
example, "ReportingVariable"
or "Observable"
.
The expression NetworkNodesInSet(net,setname)
searches through the network for all nodes labeled with the given
setname. It returns a list of such nodes.
The expression NetworkNodesInSet(net,setname)
<-value
make sure
that setname is in the node sets of all nodes that are in
value and that it is not in the node sets of any node that is
not in value.
Note that it is acceptable to use the system built-ins in the getter
method (but not the setter). For example
searching for ":TableIncomplete"
will return a collection of
nodes for which the conditional probability table has not yet been
set.
A list of nodes which are associated with the named node set.
Russell Almond
http://norsys.com/onLurl/Manual/index.html: GetAllNodesets_bn(), IsNodeInNodeset_bn()
NeticaBN
, NodeSets()
,
NetworkSetPriority()
,
NetworkNodesInSet()
, NetworkNodeSetColor()
sess <- NeticaSession() startSession(sess) nsnet <- CreateNetwork("NodeSetExample", session=sess) Ability <- NewContinuousNode(nsnet,"Ability") XX <- NewDiscreteNode(nsnet,paste("Item",1:5,sep=""),c("Right","Wrong")) X1 <- XX[[1]] EssayScore <- NewDiscreteNode(nsnet,"EssayScore",paste("level",5:0,sep="_")) Value <- NewContinuousNode(nsnet,"Value") NodeKind(Value) <- "Utility" Placement <- NewDiscreteNode(nsnet,"Placement", c("Advanced","Regular","Remedial")) NodeKind(Placement) <- "Decision" NodeSets(Ability) <- "ReportingVariable" NodeSets(X1) <- "Observable" NodeSets(EssayScore) <- c("ReportingVariable","Observable") ## setequal doesn't deal well with arbitrary objects, so ## just use the names. nodeseteq <- function(x,y) { setequal(sapply(x,NodeName),sapply(y,NodeName)) } stopifnot( nodeseteq(NetworkNodesInSet(nsnet,"ReportingVariable"), list(Ability,EssayScore)), nodeseteq(NetworkNodesInSet(nsnet,"Observable"), list(X1,EssayScore)), nodeseteq(NetworkNodesInSet(nsnet,"Observables"), list()), nodeseteq(NetworkNodesInSet(nsnet,":Nature"), c(list(Ability,EssayScore),XX)), nodeseteq(NetworkNodesInSet(nsnet,":Decision"), list(Placement)), nodeseteq(NetworkNodesInSet(nsnet,":Utility"), list(Value)) ) NetworkNodesInSet(nsnet,"TestSet") <- XX[1:3] stopifnot( is.element("TestSet",NodeSets(XX[[1]])), is.element("TestSet",NodeSets(XX[[2]])), is.element("TestSet",NodeSets(XX[[3]])), !is.element("TestSet",NodeSets(XX[[4]])), !is.element("TestSet",NodeSets(XX[[5]])) ) NetworkNodesInSet(nsnet,"TestSet") <- XX[2:4] stopifnot( !is.element("TestSet",NodeSets(XX[[1]])), is.element("TestSet",NodeSets(XX[[2]])), is.element("TestSet",NodeSets(XX[[3]])), is.element("TestSet",NodeSets(XX[[4]])), !is.element("TestSet",NodeSets(XX[[5]])) ) NetworkNodesInSet(nsnet,"TestSet") <- c(NetworkNodesInSet(nsnet,"TestSet"),XX[[5]]) stopifnot( !is.element("TestSet",NodeSets(XX[[1]])), is.element("TestSet",NodeSets(XX[[2]])), is.element("TestSet",NodeSets(XX[[3]])), is.element("TestSet",NodeSets(XX[[4]])), is.element("TestSet",NodeSets(XX[[5]])) ) DeleteNetwork(nsnet) stopSession(sess)
sess <- NeticaSession() startSession(sess) nsnet <- CreateNetwork("NodeSetExample", session=sess) Ability <- NewContinuousNode(nsnet,"Ability") XX <- NewDiscreteNode(nsnet,paste("Item",1:5,sep=""),c("Right","Wrong")) X1 <- XX[[1]] EssayScore <- NewDiscreteNode(nsnet,"EssayScore",paste("level",5:0,sep="_")) Value <- NewContinuousNode(nsnet,"Value") NodeKind(Value) <- "Utility" Placement <- NewDiscreteNode(nsnet,"Placement", c("Advanced","Regular","Remedial")) NodeKind(Placement) <- "Decision" NodeSets(Ability) <- "ReportingVariable" NodeSets(X1) <- "Observable" NodeSets(EssayScore) <- c("ReportingVariable","Observable") ## setequal doesn't deal well with arbitrary objects, so ## just use the names. nodeseteq <- function(x,y) { setequal(sapply(x,NodeName),sapply(y,NodeName)) } stopifnot( nodeseteq(NetworkNodesInSet(nsnet,"ReportingVariable"), list(Ability,EssayScore)), nodeseteq(NetworkNodesInSet(nsnet,"Observable"), list(X1,EssayScore)), nodeseteq(NetworkNodesInSet(nsnet,"Observables"), list()), nodeseteq(NetworkNodesInSet(nsnet,":Nature"), c(list(Ability,EssayScore),XX)), nodeseteq(NetworkNodesInSet(nsnet,":Decision"), list(Placement)), nodeseteq(NetworkNodesInSet(nsnet,":Utility"), list(Value)) ) NetworkNodesInSet(nsnet,"TestSet") <- XX[1:3] stopifnot( is.element("TestSet",NodeSets(XX[[1]])), is.element("TestSet",NodeSets(XX[[2]])), is.element("TestSet",NodeSets(XX[[3]])), !is.element("TestSet",NodeSets(XX[[4]])), !is.element("TestSet",NodeSets(XX[[5]])) ) NetworkNodesInSet(nsnet,"TestSet") <- XX[2:4] stopifnot( !is.element("TestSet",NodeSets(XX[[1]])), is.element("TestSet",NodeSets(XX[[2]])), is.element("TestSet",NodeSets(XX[[3]])), is.element("TestSet",NodeSets(XX[[4]])), !is.element("TestSet",NodeSets(XX[[5]])) ) NetworkNodesInSet(nsnet,"TestSet") <- c(NetworkNodesInSet(nsnet,"TestSet"),XX[[5]]) stopifnot( !is.element("TestSet",NodeSets(XX[[1]])), is.element("TestSet",NodeSets(XX[[2]])), is.element("TestSet",NodeSets(XX[[3]])), is.element("TestSet",NodeSets(XX[[4]])), is.element("TestSet",NodeSets(XX[[5]])) ) DeleteNetwork(nsnet) stopSession(sess)
Netica sets the visual appearance (i.e., colour, see
NetworkNodeSetColor()
) of a node according to highest
priority set to which the node belongs. This function changes the
order of priority.
NetworkSetPriority(net, setlist)
NetworkSetPriority(net, setlist)
net |
An active |
setlist |
A character vector containing a subset of the node set names. The first ones in the sequence will have the highest priority. |
Netica determines the visual style of a node by stepping through the
node sets to which the node belongs in priority order. Each node set
can either have a colour set, or a flag set to indicate that the next
node in order or priority should be used to determine the appearance
of the node (see NetworkNodeSetColor()
).
This function switches the priority of the node sets names in the
second argument. The node sets note mentioned in setlist
are
not affected.
Returns the net
argument invisibly.
The priority of the Netica internal node sets (the ones beginning with ‘:’) are set by Netica and cannot be changed. They all have lower priority than the user-defined node sets.
Russell Almond
http://norsys.com/onLurl/Manual/index.html: ReorderNodesets_bn(), SetNodesetColor_bn()
NeticaNode
, NodeSets()
,
NetworkNodeSets()
,
NetworkNodesInSet()
, NetworkNodeSetColor()
sess <- NeticaSession() startSession(sess) nsnet <- CreateNetwork("NodeSetExample", session=sess) Ability <- NewContinuousNode(nsnet,"Ability") EssayScore <- NewDiscreteNode(nsnet,"EssayScore",paste("level",5:0,sep="_")) Value <- NewContinuousNode(nsnet,"Value") NodeKind(Value) <- "Utility" Placement <- NewDiscreteNode(nsnet,"Placement", c("Advanced","Regular","Remedial")) NodeKind(Placement) <- "Decision" NodeSets(EssayScore) <- c("ReportingVariable","Observable") NetworkSetPriority(nsnet,c("Observable","ReportingVariable")) ## Now EssayScore should be coloured like an observable. stopifnot( NodeSets(EssayScore) == c("Observable","ReportingVariable")) NetworkSetPriority(nsnet,c("ReportingVariable","Observable")) ## Now EssayScore should be coloured like a Reporting Variable stopifnot( NodeSets(EssayScore) == c("ReportingVariable","Observable")) DeleteNetwork(nsnet) stopSession(sess)
sess <- NeticaSession() startSession(sess) nsnet <- CreateNetwork("NodeSetExample", session=sess) Ability <- NewContinuousNode(nsnet,"Ability") EssayScore <- NewDiscreteNode(nsnet,"EssayScore",paste("level",5:0,sep="_")) Value <- NewContinuousNode(nsnet,"Value") NodeKind(Value) <- "Utility" Placement <- NewDiscreteNode(nsnet,"Placement", c("Advanced","Regular","Remedial")) NodeKind(Placement) <- "Decision" NodeSets(EssayScore) <- c("ReportingVariable","Observable") NetworkSetPriority(nsnet,c("Observable","ReportingVariable")) ## Now EssayScore should be coloured like an observable. stopifnot( NodeSets(EssayScore) == c("Observable","ReportingVariable")) NetworkSetPriority(nsnet,c("ReportingVariable","Observable")) ## Now EssayScore should be coloured like a Reporting Variable stopifnot( NodeSets(EssayScore) == c("ReportingVariable","Observable")) DeleteNetwork(nsnet) stopSession(sess)
This function creates a new random number generator using the given seed and associates it with the network.
NetworkSetRNG(net, seed=sample.int(.Machine$integer.max,1L))
NetworkSetRNG(net, seed=sample.int(.Machine$integer.max,1L))
net |
An active |
seed |
An unsigned integer to be uses as the seed. |
Associating a random number generator with a Netica network has two
effects. First, if the seed is constant, then subsequent calls to
GenerateRandomCase
will create a reproducible sequence of
cases. Second, as the default random number generator Netica uses is
threadsafe, the random number generation will be slightly faster.
Returns the net
argument.
This function both creates the random number generator (see
NeticaRNG
) and associates it with the network
argument. Following the Netica API, it should be possible to separate
the two operations, but it unclear what would happen if the RNG object
was then freed (either manually or by associating it with another
network and then deleting that other network). It therefore seemed
safer to encapsulate the RNG creation process in the C code.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: NewRandomGenerator_ns(), SetNetRandomGen_bn()
NeticaRNG
,
NewNeticaRNG()
, GenerateRandomCase()
sess <- NeticaSession() startSession(sess) rnet <- CreateNetwork("Random", session=sess) NetworkSetRNG(rnet, 1234469767) DeleteNetwork(rnet) stopSession(sess)
sess <- NeticaSession() startSession(sess) rnet <- CreateNetwork("Random", session=sess) NetworkSetRNG(rnet, 1234469767) DeleteNetwork(rnet) stopSession(sess)
"NetworkTester"
The Network tester is special object that simulates data from a Bayes
net and then tests how well the values of the targetNodes
can be
recovered from evidence in the remaining nodes (excluding the nodes in
the ignoreNodes
.
The fuction testNetwork
behaves a follows: It opens a
NetworkTester
object to hold the results. It
then runs through the case streams. For each case, it instantiates
each node in the network to the findings as found in the case stream,
except for those nodes in targetNodes
or ignoreNodes
.
It then looks at the NodeBeliefs
associated with the
targetNodes
and compares these with the actual value of the
target nodes as found in the case stream. It accumulates statistics
related to the accuracy.
After all case streams are processed, the
NetworkTester
object is returned and its
statistics can be queried. See testerConfusion
for a
list of available results.
Objects can be created by calling testNetwork(targetNodes,
dataStreams, ingoreNodes)
. Here dataStreams
should be a
list of active CaseStream
objects
containing generated test data for the network.
Net
:Object of class "NeticaBN"
which
represents the network to be tested.
targetNodes
:A list of active NeticaNode
objects which represent the nodes to be tested.
ignoreNodes
:A (possibly empty) list of active
NeticaNode
objects whose values will be ignored
(not instantiated) during the tests.
data
:A list of active (open)
CaseStream
objects containing the test data.
errorRate
:A vector corresponding to the target nodes
which contains the error rates from the test (see
testerErrorRate
).
logLoss
:A vector corresponding to the target nodes
which contains the log loss rates from the test (see
testerLogLoss
).
quadraticLoss
:A vector corresponding to the target nodes
which contains the quadratic loss rates from the test (see
testerQuadraticLoss
).
confusion
:A list of matrixes corresponding to the
target nodes. Each matrix is a list corresponding to the states
of the target node with rows representing the predicted values and
actual representing the actual (simulated) value. (See
testerConfusion
).
testNetwork(targetNodes, dataStreams,
ingoreNodes=list())
:This is the de facto constructor, it runs a test on the network and stores the results in tester object.
testerNet(tester)
:Accessor for the network.
testerTarget(tester)
:Accessor for the target nodes.
testerIgnore(tester)
:Accessor for the ignored nodes.
testerErrorRate(tester,node=NULL)
:Accessor for
the error rate, if node
is supplied, will give specific
error rate, otherwise, it will give all of them.
testerLogLoss(tester,node=NULL)
:Accessor for
the log loss, if node
is supplied, will give specific
log loss, otherwise, it will give all of them.
testerQuadraticLoss(tester,node=NULL)
:Accessor for
the quadratic loss, if node
is supplied, will give specific
loss, otherwise, it will give all of them.
testerConfusion(tester,node=NULL)
:Accessor for
the confusion matrixes, if node
is supplied, will give specific
confusion matrix, otherwise, it will give a list of all of them.
testerKappa(tester,node=NULL,
weights=c("None","Linear","Quadratic"), W=NULL)
:Will calculate Cohen's Kappa for the specified node or for all of them.
testerLambda(tester,node=NULL,
weights=c("None","Linear","Quadratic"), W=NULL)
:Will calculate Goodman and Kruskal's Lambda for the specified node or for all of them.
summary.NetworkTester(object,...)
:Produces a matrix giving the error rate, log loss, quadratic loss, about both weighted and unweighted kappas and lambdas.
[Note: These are implmemented as ordinary functions and not as methods.]
Although the Netica API defines a NetTester object, this object is
both created and destroyed in the C code for
testNetwork
. That code creates the tester object, runs
the tests and extracts the results and places the cached results in an
R object. This avoids memory management issues with foreign pointers.
Note that new data cannot simply be added to the tester, as works using the Netica API. However, tester objects can have multiple data streams.
Russell Almond
Almond, R.G., Mislevy, R.J. Steinberg, L.S., Yan, D. and Willamson, D. M. (2015). Bayesian Networks in Educational Assessment. Springer. Chapter 7.
http://norsys.com/onLineAPIManual/index.html: NewNetTester_bn(), DeleteNetTester_bn(), TestWithCaseset_bn(), GetTestConfusion_bn(), GetTestErrorRate_bn(), GetTestLogLoss_bn(), GetTestQuadraticLoss_bn()
The output of testNetwork
is NetworkTester
.
Accessors for NetworkTester
testerConfusion
, testerErrorRate
,
testerIgnore
, testerKappa
,
testerLambda
, testerLogLoss
,
testerNet
, testerQuadraticLoss
,
testerTarget
sess <- NeticaSession() startSession(sess) irt5 <- ReadNetworks(system.file("sampleNets","IRT5.dne", package="RNetica"), session=sess) irt5.theta <- NetworkFindNode(irt5,"Theta") irt5.x <- NetworkFindNode(irt5,paste("Item",1:5,sep="_")) CompileNetwork(irt5) ## This generates a fixed series of random cases and saves them to a ## file. N <- 100L rnodes <- c(list(irt5.theta),irt5.x) casefile <- tempfile("irt5testcase",fileext=".cas") filestream <- CaseFileStream(casefile, session=sess) rng <- NewNeticaRNG(123456779, session=sess) WithOpenCaseStream(filestream, WithRNG(rng, for (n in 1L:N) { GenerateRandomCase(rnodes,rng=rng) WriteFindings(rnodes,filestream,n) lapply(rnodes,RetractNodeFinding) # Only retract findings for # generated nodes })) irt5.test <- testNetwork(list(irt5.theta),OpenCaseStream(filestream)) summary(irt5.test) DeleteNetwork(irt5) stopSession(sess)
sess <- NeticaSession() startSession(sess) irt5 <- ReadNetworks(system.file("sampleNets","IRT5.dne", package="RNetica"), session=sess) irt5.theta <- NetworkFindNode(irt5,"Theta") irt5.x <- NetworkFindNode(irt5,paste("Item",1:5,sep="_")) CompileNetwork(irt5) ## This generates a fixed series of random cases and saves them to a ## file. N <- 100L rnodes <- c(list(irt5.theta),irt5.x) casefile <- tempfile("irt5testcase",fileext=".cas") filestream <- CaseFileStream(casefile, session=sess) rng <- NewNeticaRNG(123456779, session=sess) WithOpenCaseStream(filestream, WithRNG(rng, for (n in 1L:N) { GenerateRandomCase(rnodes,rng=rng) WriteFindings(rnodes,filestream,n) lapply(rnodes,RetractNodeFinding) # Only retract findings for # generated nodes })) irt5.test <- testNetwork(list(irt5.theta),OpenCaseStream(filestream)) summary(irt5.test) DeleteNetwork(irt5) stopSession(sess)
The title is a longer name for a network which is not subject to the
Netica IDname
restrictions. The comment is a free form
text associated with a network.
NetworkTitle(net) NetworkTitle(net) <- value NetworkComment(net) NetworkComment(net) <- value
NetworkTitle(net) NetworkTitle(net) <- value NetworkComment(net) NetworkComment(net) <- value
net |
A |
value |
A character object giving the new title or comment. |
The title is meant to be a human readable alternative to the name,
which is not limited to the IDname
restrictions. The
title also affects how the network is displayed in the Netica GUI.
The comment is any text the user chooses to attach to the network. If value has length greater than 1, the vector is collapsed into a long string with newlines separating the components.
A character vector of length 1 providing the title or comment.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNetTitle_bn(), SetNetTitle_bn(), GetNetComments_bn(), SetNetComments_bn()
sess <- NeticaSession() startSession(sess) firstNet <- CreateNetwork("firstNet", session=sess) NetworkTitle(firstNet) <- "My First Bayesian Network" stopifnot(NetworkTitle(firstNet)=="My First Bayesian Network") now <- date() NetworkComment(firstNet)<-c("Network created on",now) ## Print here escapes the newline, so is harder to read cat(NetworkComment(firstNet),"\n") stopifnot(NetworkComment(firstNet) == paste(c("Network created on",now),collapse="\n")) DeleteNetwork(firstNet) stopSession(sess)
sess <- NeticaSession() startSession(sess) firstNet <- CreateNetwork("firstNet", session=sess) NetworkTitle(firstNet) <- "My First Bayesian Network" stopifnot(NetworkTitle(firstNet)=="My First Bayesian Network") now <- date() NetworkComment(firstNet)<-c("Network created on",now) ## Print here escapes the newline, so is harder to read cat(NetworkComment(firstNet),"\n") stopifnot(NetworkComment(firstNet) == paste(c("Network created on",now),collapse="\n")) DeleteNetwork(firstNet) stopSession(sess)
Netica maintains an internal queue of reversible operations on a
network. The NetworkUndo()
rolls them back off the stack.
The NetworkRedo()
.
NetworkUndo(net) NetworkRedo(net)
NetworkUndo(net) NetworkRedo(net)
net |
A |
The details of which operations are undoable is not clearly documented in Netica. Some obvious things, like adding nodes, do not appear to work.
Returns an invisible integer which is the return code from the underlying network function. Its value is not documented, other than it will be negative if the undo/redo stack is empty.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: UndoNetLastOper_bn(), RedoNetOper_bn()
## Not run: sess <- NeticaSession() startSession(sess) activeNet <- CreateNetwork("undoRedoTest", session=sess) NewContinuousNode(activeNet,"Node1") NewContinuousNode(activeNet,"Node2") NewContinuousNode(activeNet,"Node3") ## These tests don't actually work, I'm not sure ## what constitutes an undoable action in Netica. print(NetworkUndo(activeNet)) stopifnot(length(NetworkAllNodes(activeNet))==2) print(NetworkUndo(activeNet)) stopifnot(length(NetworkAllNodes(activeNet))==1) print(NetworkRedo(activeNet)) stopifnot(length(NetworkAllNodes(activeNet))==2) DeleteNetwork(activeNet) stopSession(sess) ## End(Not run)
## Not run: sess <- NeticaSession() startSession(sess) activeNet <- CreateNetwork("undoRedoTest", session=sess) NewContinuousNode(activeNet,"Node1") NewContinuousNode(activeNet,"Node2") NewContinuousNode(activeNet,"Node3") ## These tests don't actually work, I'm not sure ## what constitutes an undoable action in Netica. print(NetworkUndo(activeNet)) stopifnot(length(NetworkAllNodes(activeNet))==2) print(NetworkUndo(activeNet)) stopifnot(length(NetworkAllNodes(activeNet))==1) print(NetworkRedo(activeNet)) stopifnot(length(NetworkAllNodes(activeNet))==2) DeleteNetwork(activeNet) stopSession(sess) ## End(Not run)
Netica provides a mechanism for associating user defined values with a
network as a series of key/value pairs. The key must be a
IDname
and the value can be an arbitrary string
(NetworkUserField
) or arbitrary object (NetworkUserObj
).
NetworkUserField(net, fieldname) NetworkUserField(net, fieldname) <- value NetworkUserObj(net, fieldname) NetworkUserObj(net, fieldname) <- value NetworkAllUserFields(net)
NetworkUserField(net, fieldname) NetworkUserField(net, fieldname) <- value NetworkUserObj(net, fieldname) NetworkUserObj(net, fieldname) <- value NetworkAllUserFields(net)
net |
A |
fieldname |
A character scalar conforming to the |
value |
For |
Netica contains a mechanism for associating user data with networks.
In the Netica documentation, they note that only strings are really
supported as only strings are portable across implementations. The
function NetworkUserField
provides direct access for storing
strings.
The function NetworkUserObj
wraps the call to
NetworkUserField
with a call to dputToString
or
dgetFromString
to allow the serialization of arbitrary
objects.
The function NetworkUserField
returns a character scalar with
the value stored in the field fieldname
, or NA
if no
such field exists.
The function NetworkUserObj
returns an arbitrary object created
by calling dgetFromString
on the value stored in the
field fieldname
, or NULL
if no such field exists. If
the string cannot be interpreted as an R object, it generates an
error.
The function NetworkAllUserFields
returns a character vector
containing all user data stored with the network (this will be the
serialized versions of objects, not the objects themselves). The
names of the result are the names of the fields.
In his book Extending R John Chambers suggest serializing R objects through XML or JSON mechanisms rather than the older dump protocol. I may move to that later, although it will likely cause backwards compatability issues.
Russell Almond
http://norsys.com/onLineAPIManual/index.html GetNetUserField_bn(), SetNetUserField_bn(), GetNetNthUserField_bn()
NeticaBN
, NetworkComment()
NodeUserField
, dputToString()
sess <- NeticaSession() startSession(sess) userNet <- CreateNetwork("UserNet", session=sess) NetworkUserField(userNet,"Author") <- "Russell Almond" NetworkUserField(userNet,"Status") <- "In Progress" stopifnot(NetworkUserField(userNet,"Author")=="Russell Almond") stopifnot(NetworkUserField(userNet,"Status")=="In Progress") fields <- NetworkAllUserFields(userNet) stopifnot(length(fields)==2) stopifnot(all(!is.na(match(c("Russell Almond","In Progress"),fields)))) stopifnot(all(!is.na(match(c("Author","Status"),names(fields))))) stopifnot(is.na(NetworkUserField(userNet,"gender"))) stopifnot(is.null(NetworkUserObj(userNet,"gender"))) x <- sample(1L:10L) NetworkUserObj(userNet,"x") <- x x1 <- NetworkUserObj(userNet,"x") stopifnot(all(x==x1)) ## Better to use the function name, but testing storing ## the actual function NetworkUserObj(userNet,"rule") <- CPTtools::Compensatory NetworkUserObj(userNet,"rule") DeleteNetwork(userNet) stopSession(sess)
sess <- NeticaSession() startSession(sess) userNet <- CreateNetwork("UserNet", session=sess) NetworkUserField(userNet,"Author") <- "Russell Almond" NetworkUserField(userNet,"Status") <- "In Progress" stopifnot(NetworkUserField(userNet,"Author")=="Russell Almond") stopifnot(NetworkUserField(userNet,"Status")=="In Progress") fields <- NetworkAllUserFields(userNet) stopifnot(length(fields)==2) stopifnot(all(!is.na(match(c("Russell Almond","In Progress"),fields)))) stopifnot(all(!is.na(match(c("Author","Status"),names(fields))))) stopifnot(is.na(NetworkUserField(userNet,"gender"))) stopifnot(is.null(NetworkUserObj(userNet,"gender"))) x <- sample(1L:10L) NetworkUserObj(userNet,"x") <- x x1 <- NetworkUserObj(userNet,"x") stopifnot(all(x==x1)) ## Better to use the function name, but testing storing ## the actual function NetworkUserObj(userNet,"rule") <- CPTtools::Compensatory NetworkUserObj(userNet,"rule") DeleteNetwork(userNet) stopSession(sess)
Creates a new node in the NeticaBN net
. Netica Nodes
can be either discrete, in which case a list of states must be given,
or continuous, where states are not given. The function
DeleteNodes()
deletes a single node or a list of nodes.
NewDiscreteNode(net, names, states = c("Yes","No")) NewContinuousNode(net, names) DeleteNodes(nodes)
NewDiscreteNode(net, names, states = c("Yes","No")) NewContinuousNode(net, names) DeleteNodes(nodes)
net |
A |
names |
A character vector containing the name or names of the new nodes to
be created. The names must follow the |
states |
Either or character vector, or a list of character vectors. If it
is a list, its length should be the same as the length of
|
nodes |
A |
Both NewDiscreteNode()
and NewContinuousNode()
create new nodes in the network net. If names has
length greater than 1, multiple nodes are created.
Netica currently supports two types of nodes. Discrete nodes
represent nominal variables. Continuous nodes represent real
variables. Continuous nodes cannot be changed to discrete nodes (or
vise versa) using calls to the API [this is a different from the
GUI]. However, a continuous node can be made to behave like a discrete
node (or vise versa) by setting the NodeLevels()
attribute.
NewDiscreteNode()
additionally requires the states
argument to set the initial set of states. (These can be changed
later through calls to NodeStates()
). If states
is a character vector, it is used for the state names. If
names
has length greater than one, all nodes are created with
the same set of states. The default values create a collection of
binary variables. If states
is a list, then each entry should
be a character vector providing the list of states for the
corresponding new node.
The function NewContinuousNode()
creates a new continuous
node. It appears as if Netica expects continuous nodes to be used in
one of three ways: (1) they can be discretized using
NodeLevels()
, (2) they can be used as utilities, (3)
they can be used as constants. The function NodeKind()
can change a nature node (the default) to a constant or utility node.
It appears as if Netica will not compile the network unless this is
done for all nodes.
The function DeleteNode()
deletes a single node or a group of
nodes. If multiple nodes are to be deleted in a single call, they
must all belong to the same network. Node that any NeticaNode
objects that referenced the just deleted nodes will become inactive
(see is.active()
).
These functions will affect the cache of nodes maintained by the
NeticaBN
class (net$nodes
). The
creation functions will add the new nodes to the cache, and the
deletion function will remove the nodes from the cache.
For NewDiscreteNode()
or NewContinuousNode()
, this
returns either a single object of class NeticaNode
or a
list of such objects (depending on the length of names
).
For DeleteNodes()
a list of inactive NeticaNode
objects
corresponding to the recently deleted nodes. If a node was not found,
a value of NULL
will be returned instead. These will be
inactive.
Netica nodes internally contain a pointer back to the net they are
associated with (see NodeNet()
), so most functions
involving nodes don't require the net to be named. The node creation
functions are an exception.
Most functions involving lists of nodes assume that all nodes come from the same network. Netica will generate an error if this is not the case.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: NewNode_bn(), DeleteNode_bn(), GetNodeType_bn(), SetNodeLevels_bn()
CreateNetwork()
, NeticaNode
,
NodeName()
, is.discrete()
,
is.active()
, NodeStates()
,
NodeLevels()
, NodeKind()
sess <- NeticaSession() startSession(sess) safetyNet <- CreateNetwork("safetyNet", session=sess) noded1 <- NewDiscreteNode(safetyNet, "frayed") ## Yes/No stopifnot( NodeName(noded1) == "frayed", NodeStates(noded1) == c("Yes", "No"), is.discrete(noded1) ) ## Both variables should have the same set of states noded23 <- NewDiscreteNode(safetyNet,c("TensionNS","TensionEW"), c("High","Med","Low")) stopifnot( all(sapply(noded23,is.active)), all(sapply(noded23,is.discrete)), NodeNumStates(noded23[[1]]) == 3, NodeStates(noded23[[1]])==NodeStates(noded23[[2]]) ) noded45 <- NewDiscreteNode(safetyNet,c("MeshSize","RopeThickness"), list(c("Coarse","Fine"),c("Thick","Medium","Thin"))) stopifnot( all(sapply(noded45,is.active)), all(sapply(noded45,is.discrete)), NodeNumStates(noded45[[1]]) == 2, NodeNumStates(noded45[[2]]) == 3, NodeStates(noded45[[1]])!=NodeStates(noded45[[2]]) ) nodec <- NewContinuousNode(safetyNet, "Area") stopifnot( is.active(nodec), is.continuous(nodec), NodeName(nodec) == "Area" ) stopifnot(length(NetworkAllNodes(safetyNet))==6) DeleteNodes(nodec) stopifnot(length(NetworkAllNodes(safetyNet))==5) DeleteNodes(noded45) stopifnot(length(NetworkAllNodes(safetyNet))==3) DeleteNetwork(safetyNet) stopSession(sess)
sess <- NeticaSession() startSession(sess) safetyNet <- CreateNetwork("safetyNet", session=sess) noded1 <- NewDiscreteNode(safetyNet, "frayed") ## Yes/No stopifnot( NodeName(noded1) == "frayed", NodeStates(noded1) == c("Yes", "No"), is.discrete(noded1) ) ## Both variables should have the same set of states noded23 <- NewDiscreteNode(safetyNet,c("TensionNS","TensionEW"), c("High","Med","Low")) stopifnot( all(sapply(noded23,is.active)), all(sapply(noded23,is.discrete)), NodeNumStates(noded23[[1]]) == 3, NodeStates(noded23[[1]])==NodeStates(noded23[[2]]) ) noded45 <- NewDiscreteNode(safetyNet,c("MeshSize","RopeThickness"), list(c("Coarse","Fine"),c("Thick","Medium","Thin"))) stopifnot( all(sapply(noded45,is.active)), all(sapply(noded45,is.discrete)), NodeNumStates(noded45[[1]]) == 2, NodeNumStates(noded45[[2]]) == 3, NodeStates(noded45[[1]])!=NodeStates(noded45[[2]]) ) nodec <- NewContinuousNode(safetyNet, "Area") stopifnot( is.active(nodec), is.continuous(nodec), NodeName(nodec) == "Area" ) stopifnot(length(NetworkAllNodes(safetyNet))==6) DeleteNodes(nodec) stopifnot(length(NetworkAllNodes(safetyNet))==5) DeleteNodes(noded45) stopifnot(length(NetworkAllNodes(safetyNet))==3) DeleteNetwork(safetyNet) stopSession(sess)
After a network is compiled, marginal probabilities are available at each of the nodes. Entering findings changes these to probabilities associated with the conditions represented by the findings. This function returns the marginal probabilities for the variable node conditioned on the findings.
The function IsBeliefUpdated(node)
checks to see whether
the value of findings have been propagated to node yet.
NodeBeliefs(node) IsBeliefUpdated(node)
NodeBeliefs(node) IsBeliefUpdated(node)
node |
An active |
The function NodeBeliefs()
is not available until the network
has been compiled (CompileNetwork()
). Asking for
the marginal values before the network is compiled will throw an
error.
When findings are entered, the marginal probabilities (or beliefs)
associated with node will change. The process of propagating
the findings from an evidence node to a query node is known as
updating. Depending on the size and topology of the network, the
updating process might take some time. To speed up operations, the
AutoUpdate flag on the network can be cleared using
SetNetworkAutoUpdate()
.
If the AutoUpdate flag is not set for the network, then calling
NodeBeliefs(node)
could trigger an update cycle and hence take
some time. The function IsBeliefUpdated(code)
tests to see
whether the marginal probability for node currently
incorporates all of the findings. It returns true if it does and
false if not.
The function NodeBeliefs(node)
returns a vector of
probabilities of length NodeNumStates(node)
. The names
of the result are the state names.
The function IsBeliefUpdated(node)
returns TRUE
if
calling NodeBeliefs(node)
will not result in probabilities
being updated.
I tend to avoid the term "belief" because I've spent so much time writing about Dempster–Shafer models (belief functions). Netica uses it to mean the marginal probability for a node given all of the entered evidence and conditional probability tables of all of the nodes.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNodeBeliefs_bn(), IsBeliefUpdated_bn()
NeticaNode
, NeticaBN
,
NodeProbs()
,
NodeFinding()
, JointProbability()
,
MostProbableConfig()
, FindingsProbability()
NodeExpectedValue()
, NodeValue()
,
CalcNodeValue()
,
sess <- NeticaSession() startSession(sess) irt5 <- ReadNetworks(system.file("sampleNets","IRT5.dne", package="RNetica"), session=sess) irt5.theta <- NetworkFindNode(irt5,"Theta") irt5.x <- NetworkFindNode(irt5,paste("Item",1:5,sep="_")) ## Not run: NodeBeliefs(irt5.theta) ## This call will produce an errors because irt5 ## is not compiled ## End(Not run) stopifnot( !IsBeliefUpdated(irt5.theta) ) CompileNetwork(irt5) ## Ready to enter findings stopifnot ( ## irt5 is parent node, so marginal beliefs and conditional ## probability table should be the same. sum(abs(NodeBeliefs(irt5.theta) - NodeProbs(irt5.theta))) < 1e-6 ) ## Marginal probability for Node 5 irt5.x5.init <- NodeBeliefs(irt5.x[[5]]) SetNetworkAutoUpdate(irt5,TRUE) ## Automatic updating NodeFinding(irt5.x[[1]]) <- "Right" stopifnot( IsBeliefUpdated(irt5.x[[5]]) ) irt5.x5.time1 <- NodeBeliefs(irt5.x[[5]]) stopifnot ( sum(abs(irt5.x5.init-irt5.x5.time1)) > 1e-6 ) SetNetworkAutoUpdate(irt5,FALSE) ## Automatic updating NodeFinding(irt5.x[[2]]) <- "Right" stopifnot( !IsBeliefUpdated(irt5.x[[5]]) ) irt5.x5.time2 <- NodeBeliefs(irt5.x[[5]]) stopifnot ( sum(abs(irt5.x5.time2-irt5.x5.time1)) > 1e-6, IsBeliefUpdated(irt5.x[[5]]) ## Now we have updated it. ) DeleteNetwork(irt5) stopSession(sess)
sess <- NeticaSession() startSession(sess) irt5 <- ReadNetworks(system.file("sampleNets","IRT5.dne", package="RNetica"), session=sess) irt5.theta <- NetworkFindNode(irt5,"Theta") irt5.x <- NetworkFindNode(irt5,paste("Item",1:5,sep="_")) ## Not run: NodeBeliefs(irt5.theta) ## This call will produce an errors because irt5 ## is not compiled ## End(Not run) stopifnot( !IsBeliefUpdated(irt5.theta) ) CompileNetwork(irt5) ## Ready to enter findings stopifnot ( ## irt5 is parent node, so marginal beliefs and conditional ## probability table should be the same. sum(abs(NodeBeliefs(irt5.theta) - NodeProbs(irt5.theta))) < 1e-6 ) ## Marginal probability for Node 5 irt5.x5.init <- NodeBeliefs(irt5.x[[5]]) SetNetworkAutoUpdate(irt5,TRUE) ## Automatic updating NodeFinding(irt5.x[[1]]) <- "Right" stopifnot( IsBeliefUpdated(irt5.x[[5]]) ) irt5.x5.time1 <- NodeBeliefs(irt5.x[[5]]) stopifnot ( sum(abs(irt5.x5.init-irt5.x5.time1)) > 1e-6 ) SetNetworkAutoUpdate(irt5,FALSE) ## Automatic updating NodeFinding(irt5.x[[2]]) <- "Right" stopifnot( !IsBeliefUpdated(irt5.x[[5]]) ) irt5.x5.time2 <- NodeBeliefs(irt5.x[[5]]) stopifnot ( sum(abs(irt5.x5.time2-irt5.x5.time1)) > 1e-6, IsBeliefUpdated(irt5.x[[5]]) ## Now we have updated it. ) DeleteNetwork(irt5) stopSession(sess)
The children of a node parent are the nodes which are directly
connected to parent with an edge oriented from
parent. The function NodeChildren(parent)
returns a
list of the children of parent
NodeChildren(parent)
NodeChildren(parent)
parent |
A |
The function NodeChildren(parent)
only returns the immediate
descendants of parent
. A list of all descendants can be found
using the function
GetRelatedNodes(parent,"decendents")
.
The function link{NodeParents}()
returns the opposite end of the
link, however, unlike NodeParents()
, NodeChildren()
cannot be directly set.
A list (possibly empty) of NeticaNode
objects
which are the children of parent.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNodeChildren_bn()
NeticaNode
, AddLink()
,
NodeParents()
, GetRelatedNodes()
sess <- NeticaSession() startSession(sess) chnet <- CreateNetwork("ChildcareCenter", session=sess) mom <- NewContinuousNode(chnet,"Mother") stopifnot( length(NodeChildren(mom))==0 ) daughters <- NewDiscreteNode(chnet,paste("Daughter",1:3,sep="")) sapply(daughters, function(d) AddLink(mom,d)) stopifnot( length(NodeChildren(mom))==3, all(match(daughters,NodeChildren(mom),nomatch=0))>0 ) DeleteNetwork(chnet) stopSession(sess)
sess <- NeticaSession() startSession(sess) chnet <- CreateNetwork("ChildcareCenter", session=sess) mom <- NewContinuousNode(chnet,"Mother") stopifnot( length(NodeChildren(mom))==0 ) daughters <- NewDiscreteNode(chnet,paste("Daughter",1:3,sep="")) sapply(daughters, function(d) AddLink(mom,d)) stopifnot( length(NodeChildren(mom))==3, all(match(daughters,NodeChildren(mom),nomatch=0))>0 ) DeleteNetwork(chnet) stopSession(sess)
Netica contains a facility to calculate the conditional probability
table for a node from an equation. NodeEquation()
gets or sets
the equation. EquationToTable()
recalculates the conditional
probability table associated with the node.
NodeEquation(node) NodeEquation(node,autoconvert=TRUE) <- value EquationToTable(node, numSamples = 25, sampUnc = TRUE, addExist = FALSE)
NodeEquation(node) NodeEquation(node,autoconvert=TRUE) <- value EquationToTable(node, numSamples = 25, sampUnc = TRUE, addExist = FALSE)
node |
An active |
autoconvert |
A logical value that indicates whether or not the CPT should be recalculated after the equation is set. |
value |
A character value giving the equation. If it has length greater than one, it is collapsed with newlines between. |
numSamples |
In some cases Netica uses sampling to calculate the CPT. If it does, then this is the number of sample. |
sampUnc |
A logical flag indicating whether or not sampling uncertainty should be added to the values. Note that setting this to FALSE could cause zero probabilities for configurations not realized in the sampling, which may or may not be a good thing. |
addExist |
A logical flag indicating whether or not the sampled values should be added to (TRUE) or replace (FALSE) the existing CPT. Can be used to create blended CPTs. |
This is a fairly minimilistic support for Netica's equation feature. Netica equations are strings, but have a very specific syntax (see the Netica manual for details). The RNetica code does no checking before passing the value to Netica.
The function EquationToTable()
builds a conditional probability
table from the equation and must be called before Netica will update
the table used in calculations. The documentation for this function is
somewhat unclear. In particular, it is not clear when Netica uses
sampling to calculating the CPT (this should not be needed in most of
the examples I've worked with).
The use of the addExist
EquationToTable()
allows several
equations to be blended. Note that both the CPT
(NodeProbs
) and the node experience
(NodeExperience
) must be set to
There are two differences between the RNetica implementation and the
default Netica behavior. First, equations can be fairly complex. If
value is a character vector, RNetica will concatenate it into a
single string before passing it to Netica. Second, by default RNetica
automatically recalculates the table when the equation is set. This
is usually the desired behavior, but can be suppressed by setting
autoconvert=FALSE
.
Constants play a special role in Netica formulas. A formula can reference the value of a constant node even if it is not a marked parent of the node whose equation is being defined. It appears as if the value of the constant must be set before the table is created.
The function NodeEquation
returns the equation as a character
scalar. The function EquationToTable
returns the node
argument invisibly.
I personally find the Netica equation syntax to be verbose and unwieldy. I have found it easier to calculate the CPTs directly in R (using functions from the CPTtools package, CPTtools-package) and then entering those CPTs into Netica. The functions are provided here mainly for completeness.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNodeEquation_bn(), SetNodeEquation_bn(), EquationToTable_bn()
The reference document for Netica equations: http://www.norsys.com/WebHelp/NETICA/X_Equations.htm
NodeValue()
,NodeKind()
,
NodeProbs()
, Extract.NeticaNode
sess <- NeticaSession() startSession(sess) grn <- CreateNetwork("GradedResponseTest", session=sess) ## Set up the variables in our network skill <- NewDiscreteNode(grn,"Skill",c("High","Medium","Low")) NodeLevels(skill) <- c(1,0,-1) score1 <- NewDiscreteNode(grn,"Score1", c("FullCredit","PartialCredit","NoCredit")) ## Set up a couple of constants for use in formulae a1 <- NewContinuousNode(grn,"A1") NodeKind(a1) <- "Constant" b1_1 <- NewContinuousNode(grn,"B1_1") NodeKind(b1_1) <- "Constant" b1_2 <- NewContinuousNode(grn,"B1_2") NodeKind(b1_2) <- "Constant" diffB1 <- NewContinuousNode(grn,"DiffB1") NodeLevels(diffB1) <- seq(-4,4,.5) NodeValue(a1) <- 1 NodeValue(b1_1) <- -1.5 NodeValue(b1_2) <- 0 ## Note, this will generate an error if the values of the constants are ## not set first. NodeEquation(diffB1) <- "DiffB1() = B1_2 - B1_1" ## I think this should return 1.5, but it return NA. I'm not sure what ## is happening here? CalcNodeValue(diffB1) ## This is the rather clunky format for Netica formulae. This ## implements a graded response model. dsformula <- c( "p(Score1 | Skill) =", " (Score1==FullCredit)? 1/(1+exp(-1.7*(A1/sqrt(1)*Skill-B1_2))) :", " (Score1==PartialCredit) ? 1/(1+exp(-1.7*(A1/sqrt(1)*Skill-B1_1))) -", " 1/(1+exp(-1.7*(A1/sqrt(1)*Skill-B1_2))) :", "1 - 1/(1+exp(-1.7*(A1/sqrt(1)*Skill-B1_1)))" ) AddLink(skill,score1) NodeEquation(score1) <- dsformula score1[] ## Expected value: # Skill Score1.FullCredit Score1.PartialCredit Score1.NoCredit #1 High 0.8455347 0.1404016 0.01406363 #2 Medium 0.5000000 0.4275735 0.07242648 #3 Low 0.1544653 0.5461019 0.29943281 NodeValue(b1_1) <- -2 score1[] ## Change not propagated yet EquationToTable(score1) score1[] ## Now it changes DeleteNetwork(grn) stopSession(sess)
sess <- NeticaSession() startSession(sess) grn <- CreateNetwork("GradedResponseTest", session=sess) ## Set up the variables in our network skill <- NewDiscreteNode(grn,"Skill",c("High","Medium","Low")) NodeLevels(skill) <- c(1,0,-1) score1 <- NewDiscreteNode(grn,"Score1", c("FullCredit","PartialCredit","NoCredit")) ## Set up a couple of constants for use in formulae a1 <- NewContinuousNode(grn,"A1") NodeKind(a1) <- "Constant" b1_1 <- NewContinuousNode(grn,"B1_1") NodeKind(b1_1) <- "Constant" b1_2 <- NewContinuousNode(grn,"B1_2") NodeKind(b1_2) <- "Constant" diffB1 <- NewContinuousNode(grn,"DiffB1") NodeLevels(diffB1) <- seq(-4,4,.5) NodeValue(a1) <- 1 NodeValue(b1_1) <- -1.5 NodeValue(b1_2) <- 0 ## Note, this will generate an error if the values of the constants are ## not set first. NodeEquation(diffB1) <- "DiffB1() = B1_2 - B1_1" ## I think this should return 1.5, but it return NA. I'm not sure what ## is happening here? CalcNodeValue(diffB1) ## This is the rather clunky format for Netica formulae. This ## implements a graded response model. dsformula <- c( "p(Score1 | Skill) =", " (Score1==FullCredit)? 1/(1+exp(-1.7*(A1/sqrt(1)*Skill-B1_2))) :", " (Score1==PartialCredit) ? 1/(1+exp(-1.7*(A1/sqrt(1)*Skill-B1_1))) -", " 1/(1+exp(-1.7*(A1/sqrt(1)*Skill-B1_2))) :", "1 - 1/(1+exp(-1.7*(A1/sqrt(1)*Skill-B1_1)))" ) AddLink(skill,score1) NodeEquation(score1) <- dsformula score1[] ## Expected value: # Skill Score1.FullCredit Score1.PartialCredit Score1.NoCredit #1 High 0.8455347 0.1404016 0.01406363 #2 Medium 0.5000000 0.4275735 0.07242648 #3 Low 0.1544653 0.5461019 0.29943281 NodeValue(b1_1) <- -2 score1[] ## Change not propagated yet EquationToTable(score1) score1[] ## Now it changes DeleteNetwork(grn) stopSession(sess)
Calculates the expected utility for a decision node. That is for each state of the decision node it calculates the expected utility if that state is chosen.
NodeExpectedUtils(node)
NodeExpectedUtils(node)
node |
An active |
This solves a decision problem. In an influence diagram (decision net), one decision node is considered a predecessor if its value is known at the time when a decision is made. The compilation process for a decision net will fill in predecessor relationships when they are implied by paths through nature nodes. Decision networks are typically “solved” by working backwards in time from the last decision to the first.
The expression NodeExpectedUtils(node)
will only return a
meaningful result if either, node represents the first
sequential decision, or all prior decisions have been made (and their
values are known).
This should return a named numeric vector of length
NodeNumStates(node)
with each element corresponding to
one of the states of node
.
This function is currently returning an internal Netica error. Do not use until I get clarification from Norsys.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNodeExpectedUtils_bn()
For more about decision nets: http://www.norsys.com/WebHelp/NETICA/X_Decision_Problems.htm
NodeKind()
, NodeValue()
,
NodeExpectedValue()
sess <- NeticaSession() startSession(sess) ## Read the RTI network from the library. rti <- ReadNetworks(system.file("sampleNets","CostOfTesting.dne", package="RNetica"), session=sess) ## The two decision nodes Test <- NetworkFindNode(rti,"Test") Instruction <- NetworkFindNode(rti,"Instruction") ## Network must be compiled before analysis: CompileNetwork(rti) NodeExpectedUtils(Test) ## Not run: ## This produces an error because Test is not set. NodeExpectedUtils(Instruction) ## End(Not run) NodeFinding(Test) <- "Yes" NodeExpectedUtils(Instruction) DeleteNetwork(rti) stopSession(sess)
sess <- NeticaSession() startSession(sess) ## Read the RTI network from the library. rti <- ReadNetworks(system.file("sampleNets","CostOfTesting.dne", package="RNetica"), session=sess) ## The two decision nodes Test <- NetworkFindNode(rti,"Test") Instruction <- NetworkFindNode(rti,"Instruction") ## Network must be compiled before analysis: CompileNetwork(rti) NodeExpectedUtils(Test) ## Not run: ## This produces an error because Test is not set. NodeExpectedUtils(Instruction) ## End(Not run) NodeFinding(Test) <- "Yes" NodeExpectedUtils(Instruction) DeleteNetwork(rti) stopSession(sess)
Calculates the expected value for node based on the current beliefs about the nodes states. The node should either be continuous or a discrete node with levels assigned to the values. The standard deviation is supplied as an attribute.
NodeExpectedValue(node)
NodeExpectedValue(node)
node |
An active |
Returns a scalar real giving the expected value for node. It
has an attribute called "std_dev"
which contains the standard
deviation.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNodeExpectedValue_bn()
NodeBeliefs()
, NodeLevels()
,
NodeLevels()
,is.continuous()
NodeValue()
,CalcNodeValue()
,
sess <- NeticaSession() startSession(sess) irt5 <- ReadNetworks(system.file("sampleNets","IRT5.dne", package="RNetica"), session=sess) irt5.theta <- NetworkFindNode(irt5,"Theta") irt5.x <- NetworkFindNode(irt5,paste("Item",1:5,sep="_")) CompileNetwork(irt5) ## Ready to enter findings ## Prior should have mean 0, Std 1.095 stopifnot(abs(NodeExpectedValue(irt5.theta)) <.000001) stopifnot(abs(attr(NodeExpectedValue(irt5.theta),"std_dev")-1.095445)<.00001) NodeFinding(irt5.x[[1]]) <- "Right" ## Expected value should go up stopifnot(NodeExpectedValue(irt5.theta)>0) DeleteNetwork(irt5) stopSession(sess)
sess <- NeticaSession() startSession(sess) irt5 <- ReadNetworks(system.file("sampleNets","IRT5.dne", package="RNetica"), session=sess) irt5.theta <- NetworkFindNode(irt5,"Theta") irt5.x <- NetworkFindNode(irt5,paste("Item",1:5,sep="_")) CompileNetwork(irt5) ## Ready to enter findings ## Prior should have mean 0, Std 1.095 stopifnot(abs(NodeExpectedValue(irt5.theta)) <.000001) stopifnot(abs(attr(NodeExpectedValue(irt5.theta),"std_dev")-1.095445)<.00001) NodeFinding(irt5.x[[1]]) <- "Right" ## Expected value should go up stopifnot(NodeExpectedValue(irt5.theta)>0) DeleteNetwork(irt5) stopSession(sess)
In learning, if the row of the conditional probability table has a Dirichlet distribution, this sets the sum of the parameters for the row. This is the number of pseudo observations for that row of the CPT.
NodeExperience(node) NodeExperience(node) <- value
NodeExperience(node) NodeExperience(node) <- value
node |
An active |
value |
An array of pseudo counts, these should be positive values. The
shape of the array should match the
|
When learning the conditional probabilities associated with a
conditional probability table, the most general model considers each
row of the conditional probability table as an independent Dirichlet
distribution. If there are states, then the parameters of the
Dirichlet distribution are
and the expected value
is
, where
is
the normalization constant. An alternative way to represent the
Dirichlet parameters is with the probability vector and the
normalization. The experience is the normalization constant.
Note that after observing
additional observations, the
normalization constant will become
, so the experience can be
thought of as a pseudo-observation count. Finally, the variance of
the Dirichlet distribution decreases, as
increases, so it can
also be thought of as a measure of precision.
An unconditional distribution has exactly one normalization constant.
A conditional distribution has on for each row of the conditional
probability, that is associated with each possible configuration of
the parent variables. The value of NodeExperience(node)
is an
array with dimnames matching ParentStates(node)
. In
particular, this means that specific values of experience can be
accessed by using the names of the parent states.
An array whose dimnames are ParentStates(node)
. If the
node has no parents, the value is a scalar.
I tend to refer to this distribution as a "hyper-Dirichlet" distribution, although Spiegelhalter and Lauritzen (1990) used that term to refer to a network in which all of the nodes were parameterized in that way.
If the node experience has not been set, then
NodeExperience(node)
will return NA
. However, Netica
will not allow one to unset the node experience once it is set.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: SetNodeExperience_bn(), GetNodeExperience_bn()
NeticaNode
, NodeParents()
,
NodeProbs()
, CPA
sess <- NeticaSession() startSession(sess) abc <- CreateNetwork("ABC", session=sess) A <- NewDiscreteNode(abc,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc,"C",c("C1","C2")) AddLink(A,B) AddLink(A,C) AddLink(B,C) stopifnot(is.na(NodeExperience(A))) ## Parentless node, only need one value NodeExperience(A) <- 10 stopifnot( abs(NodeExperience(A)-10)<.00001 ) NodeExperience(B) <- c(1,2,3,4) stopifnot( length(NodeExperience(B))==4, all(names(NodeExperience(B))==NodeStates(A)), abs(NodeExperience(B)[2]-2)<.00001 ) ## Set them all to the same value. NodeExperience(C) <- 10 stopifnot( all(dim(NodeExperience(C))==sapply(ParentStates(C),length)), all(dimnames(NodeExperience(C))[[1]]==ParentStates(C)[[1]]), all(dimnames(NodeExperience(C))[[2]]==ParentStates(C)[[2]]), all(names(dimnames(NodeExperience(C)))==ParentNames(C)), abs(NodeExperience(C)[3,2]-10)<.00001 ) NodeExperience(C)["A3","B2"] <- 11 stopifnot( abs(NodeExperience(C)[3,2]-11)<.00001 ) DeleteNetwork(abc) stopSession(sess)
sess <- NeticaSession() startSession(sess) abc <- CreateNetwork("ABC", session=sess) A <- NewDiscreteNode(abc,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc,"C",c("C1","C2")) AddLink(A,B) AddLink(A,C) AddLink(B,C) stopifnot(is.na(NodeExperience(A))) ## Parentless node, only need one value NodeExperience(A) <- 10 stopifnot( abs(NodeExperience(A)-10)<.00001 ) NodeExperience(B) <- c(1,2,3,4) stopifnot( length(NodeExperience(B))==4, all(names(NodeExperience(B))==NodeStates(A)), abs(NodeExperience(B)[2]-2)<.00001 ) ## Set them all to the same value. NodeExperience(C) <- 10 stopifnot( all(dim(NodeExperience(C))==sapply(ParentStates(C),length)), all(dimnames(NodeExperience(C))[[1]]==ParentStates(C)[[1]]), all(dimnames(NodeExperience(C))[[2]]==ParentStates(C)[[2]]), all(names(dimnames(NodeExperience(C)))==ParentNames(C)), abs(NodeExperience(C)[3,2]-10)<.00001 ) NodeExperience(C)["A3","B2"] <- 11 stopifnot( abs(NodeExperience(C)[3,2]-11)<.00001 ) DeleteNetwork(abc) stopSession(sess)
A finding is an observed variable in a Bayesian network. The
expression NodeFinding(node) <- value
indicates that the
observed value of node should be set to value. The
function NodeFinding(node)
returns the current value.
NodeFinding(node) NodeFinding(node) <- value
NodeFinding(node) NodeFinding(node) <- value
node |
An active |
value |
A character or integer scalar indicating the value which was
observed or hypothesized. If a character, it should be one of the
values in |
Setting NodeFinding(node) <- value
essentially asserts that
. The value may be either expressed as a
character name of one of the states, or an integer giving the index
into the state table.
Note that setting NodeFinding(node) <- value
clears
any previous findings (including virtual findings set through
NodeLikelihood()
or
EnterNegativeFinding()
), that may have been set. The
function RetractNodeFinding(node)
will clear the current
finding without setting it to a new value.
The function NodeFinding(node)
returns the currently set
finding, if there is one. It can also return one of the three special
values:
"@NEGATIVE FINDINGS"
— Negative findings have been
entered using EnterNegativeFinding()
.
"@LIKELIHOOD"
— Uncertain evidence which provides a
likelihood of various states of the node were entered using
NodeLikelihood(node)
"@NO FINDING"
— No findings, including negative
findings or likelihood findings were entered.
The expression NodeFinding(node)<-value
returns the
modified node invisibly.
The function NodeFinding(node)
returns a string which is
either the currently set finding or one of the special values
"@NO FINDING"
, "@LIKELIHOOD"
, or
"@NEGATIVE FINDINGS"
.
If SetNetworkAutoUpdate()
has been set to TRUE
,
then this function could take some time as each finding is
individually propagated. Consider wrapping multiple calls setting
NodeFinding()
in WithoutAutoUpdate(net, ...)
.
Unlike the Netica function EnterFinding_bn()
the function
"NodFinding<-"
internally calls RetractFindings
. So
there is no need to do this manually.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNodeFinding_bn(), EnterFinding_bn()
NeticaBN
, NodeBeliefs()
,
EnterNegativeFinding()
, EnterFindings()
,
RetractNodeFinding()
, NodeLikelihood()
,
EnterGaussianFinding()
, EnterIntervalFinding()
,
JointProbability()
,NodeValue()
,
MostProbableConfig()
,
FindingsProbability()
sess <- NeticaSession() startSession(sess) irt5 <- ReadNetworks(system.file("sampleNets","IRT5.dne", package="RNetica"), session=sess) irt5.theta <- NetworkFindNode(irt5,"Theta") irt5.x <- NetworkFindNode(irt5,paste("Item",1:5,sep="_")) CompileNetwork(irt5) ## Ready to enter findings stopifnot ( ## irt5 is parent node, so marginal beliefs and conditional ## probability table should be the same. sum(abs(NodeBeliefs(irt5.theta) - NodeProbs(irt5.theta))) < 1e-6 ) ## Marginal probability for Node 5 irt5.x5.init <- NodeBeliefs(irt5.x[[5]]) SetNetworkAutoUpdate(irt5,TRUE) ## Automatic updating NodeFinding(irt5.x[[1]]) <- "Right" stopifnot( IsBeliefUpdated(irt5.x[[5]]) ) irt5.x5.time1 <- NodeBeliefs(irt5.x[[5]]) stopifnot ( sum(abs(irt5.x5.init-irt5.x5.time1)) > 1e-6 ) SetNetworkAutoUpdate(irt5,FALSE) ## Automatic updating NodeFinding(irt5.x[[2]]) <- 2 ## Wrong stopifnot( !IsBeliefUpdated(irt5.x[[5]]), NodeFinding(irt5.x[[2]]) == "Wrong" ) irt5.x5.time2 <- NodeBeliefs(irt5.x[[5]]) stopifnot ( sum(abs(irt5.x5.time2-irt5.x5.time1)) > 1e-6, IsBeliefUpdated(irt5.x[[5]]) ## Now we have updated it. ) ## Negative finding EnterNegativeFinding(irt5.theta,c("neg1","neg2")) ## Rule out negatives. stopifnot( NodeFinding(irt5.theta) == "@NEGATIVE FINDINGS" ) ## Clearing Findings RetractNodeFinding(irt5.theta) stopifnot( NodeFinding(irt5.theta) == "@NO FINDING" ) ##Virtual findings for X3. Assume judge has said right, but judge has ## 80% accuracy rate. NodeLikelihood(irt5.x[[3]]) <- c(.8,.2) stopifnot( NodeFinding(irt5.x[[3]]) == "@LIKELIHOOD" ) DeleteNetwork(irt5) stopSession(sess)
sess <- NeticaSession() startSession(sess) irt5 <- ReadNetworks(system.file("sampleNets","IRT5.dne", package="RNetica"), session=sess) irt5.theta <- NetworkFindNode(irt5,"Theta") irt5.x <- NetworkFindNode(irt5,paste("Item",1:5,sep="_")) CompileNetwork(irt5) ## Ready to enter findings stopifnot ( ## irt5 is parent node, so marginal beliefs and conditional ## probability table should be the same. sum(abs(NodeBeliefs(irt5.theta) - NodeProbs(irt5.theta))) < 1e-6 ) ## Marginal probability for Node 5 irt5.x5.init <- NodeBeliefs(irt5.x[[5]]) SetNetworkAutoUpdate(irt5,TRUE) ## Automatic updating NodeFinding(irt5.x[[1]]) <- "Right" stopifnot( IsBeliefUpdated(irt5.x[[5]]) ) irt5.x5.time1 <- NodeBeliefs(irt5.x[[5]]) stopifnot ( sum(abs(irt5.x5.init-irt5.x5.time1)) > 1e-6 ) SetNetworkAutoUpdate(irt5,FALSE) ## Automatic updating NodeFinding(irt5.x[[2]]) <- 2 ## Wrong stopifnot( !IsBeliefUpdated(irt5.x[[5]]), NodeFinding(irt5.x[[2]]) == "Wrong" ) irt5.x5.time2 <- NodeBeliefs(irt5.x[[5]]) stopifnot ( sum(abs(irt5.x5.time2-irt5.x5.time1)) > 1e-6, IsBeliefUpdated(irt5.x[[5]]) ## Now we have updated it. ) ## Negative finding EnterNegativeFinding(irt5.theta,c("neg1","neg2")) ## Rule out negatives. stopifnot( NodeFinding(irt5.theta) == "@NEGATIVE FINDINGS" ) ## Clearing Findings RetractNodeFinding(irt5.theta) stopifnot( NodeFinding(irt5.theta) == "@NO FINDING" ) ##Virtual findings for X3. Assume judge has said right, but judge has ## 80% accuracy rate. NodeLikelihood(irt5.x[[3]]) <- c(.8,.2) stopifnot( NodeFinding(irt5.x[[3]]) == "@LIKELIHOOD" ) DeleteNetwork(irt5) stopSession(sess)
The function NodeInputNames()
can be used to set or retrieve
names for each of the parents of node. This facilitates
operations such as copying and reconnecting the nodes.
NodeInputNames(node) NodeInputNames(node) <- value
NodeInputNames(node) NodeInputNames(node) <- value
node |
A |
value |
A character vector of length |
When a parent node is detached from a child, Netica names the link
with the name of the old node. For example, suppose that the
following commands were executed AddLink(A,C); AddLink(B,C)
.
Then if the node B
is detached, via
NodeParents(C)[2]<-list(NULL)
, Netica will replace B
with a stub node, and name the link "B"
. The command
NodeParents(C)$B <- D
would then attach the node D
where
the old node was attached.
Rather than relying on the automatic naming scheme, the node names can
be directly set using
NodeInputNames(node)<-newvals
. Netica
will not rename a detached link if there already exists a name for
that link. Explicitly naming the links rather than relying on
Netica's naming scheme is probably good practice. If node input names
are set, then they will be used names for the return value of
NodeParents()
The getter form NodeInputNames()
returns the currently set
names of the input links. If an input link whose name has not been
set either directly or via inserting a NULL
in
NodeParents()
has a name of ""
.
The function NodeInputNames()
returns a character vector of
the same length as GetNodeParents()
giving the current names of
the links. If a link has not yet been named, the corresponding entry
of the vector will be the empty string.
The setter function returns the node object invisibly.
To detach a parent, you must use list(NULL)
on the left hand
side of NodeParents(node)[i] <- list(NULL)
and not
NULL
.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNodeInputNames_bn(), SetNodeInputNames_bn(), SwitchNodeParent_bn()
NeticaNode
, AddLink()
,
NodeParents()
sess <- NeticaSession() startSession(sess) abnet <- CreateNetwork("AB", session=sess) anodes <- NewDiscreteNode(abnet, paste("A",1:3,sep="")) B <- NewDiscreteNode(abnet,"B") NodeParents(B) <- anodes stopifnot( all(NodeInputNames(B)=="") ) NodeParents(B)[2] <- list(NULL) stopifnot( NodeInputNames(B)==c("","A2","") ) ## Now can use A2 as name D <- NewDiscreteNode(abnet,"D") NodeParents(B)$A2 <- D ## But name doesn't change stopifnot( NodeInputNames(B)==c("","A2","") ) ##Name the inputs NodeInputNames(B) <- paste("Input",1:3,sep="") stopifnot( names(NodeParents(B))[2]=="Input2" ) ## Now detaching nodes doesn't change input names. NodeParents(B)[1] <- list(NULL) stopifnot( NodeKind(NodeParents(B)[[1]])=="Stub", NodeInputNames(B)[1]=="Input1" ) DeleteNetwork(abnet) stopSession(sess)
sess <- NeticaSession() startSession(sess) abnet <- CreateNetwork("AB", session=sess) anodes <- NewDiscreteNode(abnet, paste("A",1:3,sep="")) B <- NewDiscreteNode(abnet,"B") NodeParents(B) <- anodes stopifnot( all(NodeInputNames(B)=="") ) NodeParents(B)[2] <- list(NULL) stopifnot( NodeInputNames(B)==c("","A2","") ) ## Now can use A2 as name D <- NewDiscreteNode(abnet,"D") NodeParents(B)$A2 <- D ## But name doesn't change stopifnot( NodeInputNames(B)==c("","A2","") ) ##Name the inputs NodeInputNames(B) <- paste("Input",1:3,sep="") stopifnot( names(NodeParents(B))[2]=="Input2" ) ## Now detaching nodes doesn't change input names. NodeParents(B)[1] <- list(NULL) stopifnot( NodeKind(NodeParents(B)[[1]])=="Stub", NodeInputNames(B)[1]=="Input1" ) DeleteNetwork(abnet) stopSession(sess)
Netica supports nodes of four different kinds: "Nature"
,
"Decision"
, "Utility"
, and "Constant"
. A fifth
kind, "Stub"
is used for a reference to a node when an edge has
been detached from a node. The function NodeKind()
returns the
current kind.
NodeKind(node) NodeKind(node) <- value
NodeKind(node) NodeKind(node) <- value
node |
A |
value |
A character string with one of the values: |
A "Nature"
node (the default when the node is created) is a
random variable whose value can be predicted using the network. Pure
Bayesian networks use only "Nature"
nodes.
A "Decision"
node is one whose value will be chosen by some
decision maker. A "Utility"
node is one whose value the
decision maker is trying to optimize. A influence diagram contains
decision nodes and utilities in addition to nature nodes. The goal is
implicitly to find a setting of the decision nodes that maximizes the
expected utility.
A "Constant"
node is a parameter used for building a
conditional probability table. Its value is nominally fixed, but it
can be changed to perform sensitivity analysis.
A "Stub"
is a reference to a node created by removing a parent
node from another node without changing the table. It is assumed that
a real node will later be attached in that location. This kind can
only be set internally to Netica; the expression
NodeKind(node) <- "Stub"
will generate an error.
A character vector of length one containing one of the values:
"Nature"
, "Decision"
, "Utility"
,
"Constant"
, or "Stub"
.
Internal to Netica, "Stub"
s are called
DISCONNECTED_NODE
s. I changed the name to make them start with
a unique letter.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNodeKind_bn(), SetNodeKind_bn()
NeticaNode
, is.discrete()
,
NodeParents()
sess <- NeticaSession() startSession(sess) knet <- CreateNetwork("kNet", session=sess) skills <- NewContinuousNode(knet,paste("SkillAtTime",1:2,sep="")) reward <- NewContinuousNode(knet,"RewardForSkill") NodeKind(reward) <- "Utility" placement <- NewDiscreteNode(knet,"Placement",c("Tier1","Tier2","Tier3")) NodeKind(placement) <- "Decision" instructionCost <- NewContinuousNode(knet,"CostOfInstruction") NodeKind(instructionCost) <- "U" pretest <- NewDiscreteNode(knet,"PretestDecision",c("yes","no")) NodeKind(pretest) <- "D" pretestScore <- NewContinuousNode(knet,"PretestScore") NodeKind(pretestScore) <- "Nature" pretestCost <- NewContinuousNode(knet,"PretestCost") NodeKind(pretestCost) <- "u" pretestR <- NewContinuousNode(knet,"PretestReliability") NodeKind(pretestR) <- "Constant" stopifnot( NodeKind(skills[[1]]) == "Nature", NodeKind(skills[[2]]) == "Nature", NodeKind(reward) == "Utility", NodeKind(placement) == "Decision", NodeKind(instructionCost) == "Utility", NodeKind(pretest) == "Decision", NodeKind(pretestScore) == "Nature", NodeKind(pretestCost) == "Utility", NodeKind(pretestR) == "Constant" ) ## To make stub node, need links AddLink(skills[[1]],pretestScore) NodeInputNames(pretestScore) <- "SkillTested" ## Detach node NodeParents(pretestScore)$SkillTested <- list(NULL) stopifnot( NodeKind(NodeParents(pretestScore)$SkillTested) == "Stub" ) DeleteNetwork(knet) stopSession(sess)
sess <- NeticaSession() startSession(sess) knet <- CreateNetwork("kNet", session=sess) skills <- NewContinuousNode(knet,paste("SkillAtTime",1:2,sep="")) reward <- NewContinuousNode(knet,"RewardForSkill") NodeKind(reward) <- "Utility" placement <- NewDiscreteNode(knet,"Placement",c("Tier1","Tier2","Tier3")) NodeKind(placement) <- "Decision" instructionCost <- NewContinuousNode(knet,"CostOfInstruction") NodeKind(instructionCost) <- "U" pretest <- NewDiscreteNode(knet,"PretestDecision",c("yes","no")) NodeKind(pretest) <- "D" pretestScore <- NewContinuousNode(knet,"PretestScore") NodeKind(pretestScore) <- "Nature" pretestCost <- NewContinuousNode(knet,"PretestCost") NodeKind(pretestCost) <- "u" pretestR <- NewContinuousNode(knet,"PretestReliability") NodeKind(pretestR) <- "Constant" stopifnot( NodeKind(skills[[1]]) == "Nature", NodeKind(skills[[2]]) == "Nature", NodeKind(reward) == "Utility", NodeKind(placement) == "Decision", NodeKind(instructionCost) == "Utility", NodeKind(pretest) == "Decision", NodeKind(pretestScore) == "Nature", NodeKind(pretestCost) == "Utility", NodeKind(pretestR) == "Constant" ) ## To make stub node, need links AddLink(skills[[1]],pretestScore) NodeInputNames(pretestScore) <- "SkillTested" ## Detach node NodeParents(pretestScore)$SkillTested <- list(NULL) stopifnot( NodeKind(NodeParents(pretestScore)$SkillTested) == "Stub" ) DeleteNetwork(knet) stopSession(sess)
The levels associate a numeric value with the levels of a discrete
NeticaNode
, or cut a discrete node into a number
ordered categories. This function fetches or retrieves the levels
for node. See description for more details.
NodeLevels(node) NodeLevels(node) <- value
NodeLevels(node) NodeLevels(node) <- value
node |
A |
value |
A numeric vector of values. For discrete nodes, values
should have length |
The behavior of the levels depends on whether the node is discrete
(is.discrete(node)==TRUE
) or continuous
(is.discrete(node)==TRUE
).
Discrete. For discrete nodes, the levels are associated with
the states and provide a numeric summary of the states. In
particular, if NodeLevels
are set, then it is meaningful to
calculate an expected value for the node. The vector returned by
NodeLevels()
is named with the names of the states, making
the association clear. When setting the NodeLevels
, it
should have length equal to the number of states
(NodeNumStates(node)
).
Note that the first time the NodeLevels()
are set, the entire
vector must be set. After that point individual values may be
changed.
Continuous. For a continuous node, the levels are used to
split the continuous range into intervals (similar in spirit to the
function cut()
). The levels represent the
endpoints of the intervals and should be in either
increasing or decreasing order. The values Inf
and
-Inf
are acceptable for the endpoints of the interval. There
should be one more level than the desired number of states.
The states of a continuous node are defined by the node levels, and it
is not meaningful to try to set NodeStates()
,
NodeStateTitles()
or NodeStateComments()
.
Setting NodeLevels(node)<-NULL
for a continuous node will clear
the levels and the states.
For discrete nodes, a numeric vector of length NodeNumStates()
,
with names equal to the state names. If levels have not be set, NAs
will be returned.
For continuous nodes, a numeric vector of length
NodeNumStates()+1
with no names, or character(0)
.
The overloading of node levels is a "feature" of the Netica API. It is not great design, but it probably will be maintained for backwards compatibility.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: SetNodeLevels_bn()(), GetNodeLevels_bn(), GetNodeNumberStates_bn(), GetNodeStateName_bn(), SetNodeStateNames_bn()
NewDiscreteNode()
, NeticaNode
,
NodeName()
, is.discrete()
,
is.active()
, NodeStateTitles()
,
NodeStates()
, NodeStateComments()
,
sess <- NeticaSession() startSession(sess) lnet <- CreateNetwork("LeveledNet", session=sess) ## Discrete Node vnode <- NewDiscreteNode(lnet,"volt_switch",c("Off","Reverse","Forwards")) stopifnot( length(NodeLevels(vnode))==3, names(NodeLevels(vnode)) == NodeStates(vnode), all(is.na(NodeLevels(vnode))) ) ## Not run: ## Don't run this until the levels for vnode have been set, ## it will generate an error. NodeLevels(vnode)[2] <- 0 ## End(Not run) NodeLevels(vnode) <- 1:3 stopifnot( length(NodeLevels(vnode))==3, names(NodeLevels(vnode)) == NodeStates(vnode), NodeLevels(vnode)[2]==2 ) NodeLevels(vnode)["Reverse"] <- -2 ## Continuous Node wnode <- NewContinuousNode(lnet,"Weight") stopifnot( length(NodeLevels(wnode))==0, NodeNumStates(wnode)==0 ) NodeLevels(wnode) <- c(0, 0.1, 10, Inf) stopifnot( length(NodeStates(wnode))==3, NodeNumStates(wnode)==3 ) NodeStates(wnode) <- c("Low","Medium","High") stopifnot( NodeStates(wnode)[3] == "High", is.null(names(NodeLevels(wnode))) ) ## Change number of states NodeLevels(wnode) <- c(0, 0.1, 10, 100, Inf) stopifnot( length(NodeStates(wnode))==4, NodeNumStates(wnode)==4, all(nchar(NodeStates(wnode))==0) ) ## Clear levels NodeLevels(wnode) <- c() stopifnot( NodeNumStates(wnode)==0, length(NodeStates(wnode))==0 ) DeleteNetwork(lnet) stopSession(sess)
sess <- NeticaSession() startSession(sess) lnet <- CreateNetwork("LeveledNet", session=sess) ## Discrete Node vnode <- NewDiscreteNode(lnet,"volt_switch",c("Off","Reverse","Forwards")) stopifnot( length(NodeLevels(vnode))==3, names(NodeLevels(vnode)) == NodeStates(vnode), all(is.na(NodeLevels(vnode))) ) ## Not run: ## Don't run this until the levels for vnode have been set, ## it will generate an error. NodeLevels(vnode)[2] <- 0 ## End(Not run) NodeLevels(vnode) <- 1:3 stopifnot( length(NodeLevels(vnode))==3, names(NodeLevels(vnode)) == NodeStates(vnode), NodeLevels(vnode)[2]==2 ) NodeLevels(vnode)["Reverse"] <- -2 ## Continuous Node wnode <- NewContinuousNode(lnet,"Weight") stopifnot( length(NodeLevels(wnode))==0, NodeNumStates(wnode)==0 ) NodeLevels(wnode) <- c(0, 0.1, 10, Inf) stopifnot( length(NodeStates(wnode))==3, NodeNumStates(wnode)==3 ) NodeStates(wnode) <- c("Low","Medium","High") stopifnot( NodeStates(wnode)[3] == "High", is.null(names(NodeLevels(wnode))) ) ## Change number of states NodeLevels(wnode) <- c(0, 0.1, 10, 100, Inf) stopifnot( length(NodeStates(wnode))==4, NodeNumStates(wnode)==4, all(nchar(NodeStates(wnode))==0) ) ## Clear levels NodeLevels(wnode) <- c() stopifnot( NodeNumStates(wnode)==0, length(NodeStates(wnode))==0 ) DeleteNetwork(lnet) stopSession(sess)
The findings associated with a node can be expressed as the probability of the evidence occurring in each of the states of the node. This is the likelihood associated with the node. This function retrieves or sets the likelihood.
NodeLikelihood(node) NodeLikelihood(node) <- value
NodeLikelihood(node) NodeLikelihood(node) <- value
node |
An active |
value |
A numeric vector of length |
This function retrieves or sets virtual evidence associated with each
node. Suppose that some set of evidence is observed. The
each of the values in the likelihood represents the conditional
probability
. Note that the likelihood can be
thought of as the message that a new node child which was a
child of node with no other parents would pass to node
if its value was set.
As the likelihood values are conditional probabilities, they do not need to add to 1, although they are still restricted to the range [0,1]. Also, at least one value must be non-zero (this represents an impossible case) or Netica will generpate an error.
Entering findings through NodeFinding(node) <-
state
sets a special likelihood. In this case, the likelihood value
corresponding to state will be one, and all others will be zero.
Similarly, the expression
EnterNegativeFinding(node,statelist)
sets a
special likelihood with 0's corresponding to the states in
statelist and 1's elsewhere.
Setting the likelihood calls RetractNodeFinding()
,
clearing any previous finding, negative finding or likelihood.
The function NodeLikelihood(node)
returns a vector of
likelihoods of length NodeNumStates(node)
. The names
of the result are the state names.
The expression NodeLikelihood(node)<-value
returns
the modified node invisibly.
The documentation for the Netica function MostProbableConfig_bn()
states that likelihood findings are not
properly taken into account in MostProbableConfig()
.
Some quick tests indicate that it is doing something sensible, but
more extensive testing and/or clarification is needed.
The documentation for the Netica function
FindingsProbability_bn() also provides a warning about likelihood
evidence. The function FindingsProbability(net)
still
gives a result, but it is the normalization constant for the network,
and not necessarily a probability.
If SetNetworkAutoUpdate()
has been set to TRUE
,
then setting the likelihood could take some time as each finding is
individually propagated. Consider wrapping multiple calls setting
NodeLikelihood()
in WithoutAutoUpdate(net, ...)
.
Unlike the Netica function EnterNodeLikelihood_bn()
the function
"NodeLikelihood<-"
internally calls RetractFindings
. So
there is no need to do this manually.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNodeLikelihood_bn(), EnterNodeLikelihood_bn()
NeticaBN
, NodeBeliefs()
,
EnterNegativeFinding()
,
RetractNodeFinding()
, NodeFinding()
JointProbability()
,
MostProbableConfig()
,
FindingsProbability()
sess <- NeticaSession() startSession(sess) irt5 <- ReadNetworks(system.file("sampleNets","IRT5.dne", package="RNetica"), session=sess) irt5.theta <- NetworkFindNode(irt5,"Theta") irt5.x <- NetworkFindNode(irt5,paste("Item",1:5,sep="_")) CompileNetwork(irt5) ## Ready to enter findings ## Simple finding NodeFinding(irt5.x[[1]])<-"Wrong" stopifnot( NodeLikelihood(irt5.x[[1]]) == c(0,1) ) ## Negative finding EnterNegativeFinding(irt5.theta,c("neg1","neg2")) ## Rule out negatives. stopifnot( NodeLikelihood(irt5.x[[1]]) == c(0,1), NodeLikelihood(irt5.theta) == c(1,1,1,0,0), NodeFinding(irt5.theta) == "@NEGATIVE FINDINGS" ) ## Clearing Findings RetractNodeFinding(irt5.theta) stopifnot( NodeLikelihood(irt5.theta) == c(1,1,1,1,1) ) ##Virtual findings for X3. Assume judge has said right, but judge has ## 80% accuracy rate. NodeLikelihood(irt5.x[[3]]) <- c(.8,.2) stopifnot( sum(abs(NodeLikelihood(irt5.x[[3]]) - c(.8,.2))) < 1e-6, NodeFinding(irt5.x[[3]]) == "@LIKELIHOOD" ) ## Add in virtual likelihood from a second judge NodeLikelihood(irt5.x[[3]]) <- NodeLikelihood(irt5.x[[3]]) * c(.75,.25) stopifnot( sum(abs(NodeLikelihood(irt5.x[[3]]) - c(.6,.05))) < 1e-6 ) DeleteNetwork(irt5) stopSession(sess)
sess <- NeticaSession() startSession(sess) irt5 <- ReadNetworks(system.file("sampleNets","IRT5.dne", package="RNetica"), session=sess) irt5.theta <- NetworkFindNode(irt5,"Theta") irt5.x <- NetworkFindNode(irt5,paste("Item",1:5,sep="_")) CompileNetwork(irt5) ## Ready to enter findings ## Simple finding NodeFinding(irt5.x[[1]])<-"Wrong" stopifnot( NodeLikelihood(irt5.x[[1]]) == c(0,1) ) ## Negative finding EnterNegativeFinding(irt5.theta,c("neg1","neg2")) ## Rule out negatives. stopifnot( NodeLikelihood(irt5.x[[1]]) == c(0,1), NodeLikelihood(irt5.theta) == c(1,1,1,0,0), NodeFinding(irt5.theta) == "@NEGATIVE FINDINGS" ) ## Clearing Findings RetractNodeFinding(irt5.theta) stopifnot( NodeLikelihood(irt5.theta) == c(1,1,1,1,1) ) ##Virtual findings for X3. Assume judge has said right, but judge has ## 80% accuracy rate. NodeLikelihood(irt5.x[[3]]) <- c(.8,.2) stopifnot( sum(abs(NodeLikelihood(irt5.x[[3]]) - c(.8,.2))) < 1e-6, NodeFinding(irt5.x[[3]]) == "@LIKELIHOOD" ) ## Add in virtual likelihood from a second judge NodeLikelihood(irt5.x[[3]]) <- NodeLikelihood(irt5.x[[3]]) * c(.75,.25) stopifnot( sum(abs(NodeLikelihood(irt5.x[[3]]) - c(.6,.05))) < 1e-6 ) DeleteNetwork(irt5) stopSession(sess)
Gets or sets the name of the node. Names must conform to the
IDname
rules.
NodeName(node, internal=FALSE) NodeName(node)<- value
NodeName(node, internal=FALSE) NodeName(node)<- value
node |
An active |
internal |
A logical scalar. If true, the actual Netica object will be consulted, if false, a cached value in the R object will be used. |
value |
An character vector of length 1 giving the new name. |
Node names must conform to the IDname
rules for
Netica identifiers. Trying to set the node to a name that does not
conform to the rules will produce an error, as will trying to set the
node name to a name that corresponds to a different node in the network.
On a call to the setting method, if a node of the given name already
exists, a warning will be issued and the node
argument will be
returned unchanged.
The NodeTitle()
function provides another way to name
a node which is not subject to the IDname
restrictions.
Note that the name of the node is stored in two places: in the
Name
field of the NeticaNode
object
(node$Name
), and internally in the Netica object. These
should be the same; however, may not be. The internal
field is
used to force a check of the internal Netica object rather than the
field in the R object.
The name of the node as a character vector of length 1.
The setter method returns the NeticaNode
object.
This paragraph is obsolete as of RNetica version 0.5, it describes the previous versions only.
NeticaNode
objects are internally implemented as character vectors
giving the name of the network. If a node is renamed, then it is
possible that R will hold onto an old reference that still using the
old name. In this case, NodeName(node)
will give the correct
name, and NetworkFindNode(net,NodeName(node))
will return a
reference to a corrected object.
Starting with RNetica 0.5, NeticaNode
objects are
cached in the NeticaBN
object. The setter
method for NodeName
updates the cache as well.
In versions of RNetica less than 0.5, trying to set the name of a node to a name that was already used would generate a warning instead of an error. It now generates an error.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNodeName_bn(), SetNodeName_bn()
NewDiscreteNode()
, NeticaNode
,
NetworkFindNode()
, NodeTitle()
,
NeticaBN
sess <- NeticaSession() startSession(sess) net <- CreateNetwork("funNet", session=sess) pnode <- NewDiscreteNode(net,"play") nodecached <- pnode stopifnot(NodeName(pnode)=="play") stopifnot(NodeName(pnode,internal=TRUE)=="play") stopifnot(net$findNode("play")==pnode) stopifnot(net$nodes$play==pnode) NodeName(pnode)<-"work" stopifnot(pnode$Name=="work") stopifnot(is.null(net$findNode("play"))) stopifnot(net$nodes$work==pnode) stopifnot(NodeName(pnode) == NodeName(nodecached)) stopifnot(NodeName(pnode) == NodeName(nodecached,internal=TRUE)) snode <- NewContinuousNode(net,"sleep") cat("Next statement should generate an error message.\n") nn <- try(NodeName(snode)<- "work") ## This should raise an error stopifnot(is(nn,"try-error")) allNodes <- NetworkAllNodes(net) NodeName(allNodes$work) <- "effort" stopifnot(net$nodes$effort == pnode) DeleteNetwork(net) stopSession(sess)
sess <- NeticaSession() startSession(sess) net <- CreateNetwork("funNet", session=sess) pnode <- NewDiscreteNode(net,"play") nodecached <- pnode stopifnot(NodeName(pnode)=="play") stopifnot(NodeName(pnode,internal=TRUE)=="play") stopifnot(net$findNode("play")==pnode) stopifnot(net$nodes$play==pnode) NodeName(pnode)<-"work" stopifnot(pnode$Name=="work") stopifnot(is.null(net$findNode("play"))) stopifnot(net$nodes$work==pnode) stopifnot(NodeName(pnode) == NodeName(nodecached)) stopifnot(NodeName(pnode) == NodeName(nodecached,internal=TRUE)) snode <- NewContinuousNode(net,"sleep") cat("Next statement should generate an error message.\n") nn <- try(NodeName(snode)<- "work") ## This should raise an error stopifnot(is(nn,"try-error")) allNodes <- NetworkAllNodes(net) NodeName(allNodes$work) <- "effort" stopifnot(net$nodes$effort == pnode) DeleteNetwork(net) stopSession(sess)
Each active NeticaNode
object lives inside of a
NeticaBN
object. This function finds the network
corresponding to a node.
NodeNet(node, internal=FALSE)
NodeNet(node, internal=FALSE)
node |
A |
internal |
A logical scalar. If true, the actual Netica object will be consulted, if false, a cached value in the R object will be used. |
Two nodes with the same details in different networks are not
identical inside of Netica. Nodes are always constructed inside of
nets, and the Net
field of a node cannot be changed. (See
CopyNodes
for copying a node to a new network.)
Starting with RNetica version 0.5, a NeticaNode
object can figure out its network in two different ways. First the
field node$Net
has the NeticaBN
object associated with this node. The second is by going into the
Netica node object, finding the corresponding network and then looking
it up by name in the NeticaSession
object. With
the option internal=TRUE
this is what is done to check the
node.
The node must be active. If is.active(node)
returns false, this function will return NULL
. Note that the
expression node$Net
will return the (possible inactive)
NeticaBN
object that the node used to belong to.
The functions NetworkAllNodes()
and
NetworkFindNode()
provide pseudo-inverses for this
function.
A NeticaBN
object which contains node
, or
NULL
if node
is not active and the internal method was
selected.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNodeNet_bn()
NeticaBN
, NeticaNode
,
is.active()
, NetworkAllNodes()
,
NetworkFindNode()
sess <- NeticaSession() startSession(sess) neta <- CreateNetwork("Net_A", session=sess) netb <- CreateNetwork("Net_B", session=sess) nodea <- NewContinuousNode(neta,"Node") nodeb <- NewContinuousNode(netb,"Node") stopifnot(NodeNet(nodea)==neta) stopifnot(NodeNet(nodeb)==netb) stopifnot(NodeNet(nodea)==NodeNet(nodea,internal=TRUE)) ## Note stopifnot(nodea != nodeb) ## But: stopifnot(nodea$Name == nodeb$Name) DeleteNodes(nodeb) stopifnot(is.null(NodeNet(nodeb))) stopifnot(nodeb$Net==netb) DeleteNodes(nodea) DeleteNetwork(list(neta,netb)) stopSession(sess)
sess <- NeticaSession() startSession(sess) neta <- CreateNetwork("Net_A", session=sess) netb <- CreateNetwork("Net_B", session=sess) nodea <- NewContinuousNode(neta,"Node") nodeb <- NewContinuousNode(netb,"Node") stopifnot(NodeNet(nodea)==neta) stopifnot(NodeNet(nodeb)==netb) stopifnot(NodeNet(nodea)==NodeNet(nodea,internal=TRUE)) ## Note stopifnot(nodea != nodeb) ## But: stopifnot(nodea$Name == nodeb$Name) DeleteNodes(nodeb) stopifnot(is.null(NodeNet(nodeb))) stopifnot(nodeb$Net==netb) DeleteNodes(nodea) DeleteNetwork(list(neta,netb)) stopSession(sess)
A parent of a NeticaNode
is another node which has a
link (created through AddLink()
from that node to
child. This function returns the list of parents. It also
allows the list of parents for the node to be set, altering the
topology of the network (see details).
NodeParents(child) NodeParents(child) <- value
NodeParents(child) NodeParents(child) <- value
child |
An active |
value |
A list of |
At its most basic level, NodeParents()
reports on the topology
of a network. Suppose we add the links A1 --> B
,
A2 --> B
, and A3 --> B
to the network. Then
NodeParents(B)
should return list(A1, A2, A3)
. The
order of the inputs is important, because that this determines the
order of the dimensions in the conditional probability table
(NodeProbs()
).
The parent list can be set. This can accomplishes a number of
different goals: it can replace a parent variable, it can add
additional parents, it can remove extra parents, and it can reorder
parents. Changing the parents alters the topology of the network.
Note that Netica networks must always be acyclic directed graphs. In
particular, if
is.NodeRelated(child,"decendent",parent)
returns true for any prospective parent, Netica will generate an error
(new parents must node be descendants of the child as that would
produce a cycle).
Setting an element of the parent list to list(NULL)
has special
semantics. In this case, the parent node becomes a special stub
node (or DISCONNECTED_TYPE, see NodeKind()
). This
creates a Bayesian network fragment which can later be connected to
another Bayesian network (using SetParents()
with the new
parent.
The function NodeInputNames(child)
, returns a list of
names for the parent variables. Naming the parent variables
facilitates disconnecting the node and reconnecting it. Whenever a
node is disconnected, the corresponding input is named after the
disconnected node, unless it already has an input name.
A list of NeticaNode
objects representing the
parents in the order that they will be used to establish dimensions
for the conditional probability table. If
NodeInputNames(child)
has been set, the names of the result
will be the input names.
The setting variant returns the modified child object.
Much of the checking for this function is done internally in the Netica API, and not in the RNetica interface layer. In particular, creating directed cycles will produce errors in Netica and not in RNetica.
This is actually an attempt to make the RNetica interface more R-like,
covering the common cases of NodeParents(child) <- value
.
Under the hood it is using the Netica function
SwitchNodeParent_bn()
to produce the expected behavior.
The fact that if x
is a list x[[2]]<-NULL
deletes the
second element rather than replacing it with NULL
is a serious
design flaw in R. However, it is documented in the FAQ and it is
unlikely to change, so we need to workaround it. We do this by
setting the element we want to delete to list(NULL)
.
Nominally, we would do this through x[2]<-list(NULL)
, which is
the official workaround for the design flaw. NodeParents<-
will accept list(NULL)
in place of NULL
because nobody
who isn't part of the R Core Development Team will ever remember which
form they are suppose to use here.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNodeParents_bn(), SwitchNodeParent_bn()
NeticaNode
, AddLink()
,
NodeChildren()
, NodeKind()
,
NodeInputNames()
, is.NodeRelated()
sess <- NeticaSession() startSession(sess) abnet <- CreateNetwork("AB", session=sess) anodes <- NewDiscreteNode(abnet, paste("A",1:3,sep="")) B <- NewDiscreteNode(abnet,"B") ## Should be empty list stopifnot(length(NodeParents(B))==0) NodeParents(B) <- anodes stopifnot( length(NodeParents(B))==3, NodeParents(B)[[2]] == anodes[[2]] ) ## Reorder nodes NodeParents(B) <- anodes[c(2:3,1)] stopifnot( length(NodeParents(B))==3, NodeName(NodeParents(B)[[2]])=="A3", all(nchar(names(NodeParents(B)))==0) ) ## Remove a node. NodeParents(B) <- anodes[2:1] stopifnot( length(NodeParents(B))==2, NodeName(NodeParents(B)[[2]])=="A1", all(nchar(names(NodeParents(B)))==0) ) ## Add a node NodeParents(B) <- anodes[3:1] stopifnot( length(NodeParents(B))==3, NodeName(NodeParents(B)[[3]])=="A1", all(nchar(names(NodeParents(B)))==0) ) ##Name the inputs NodeInputNames(B) <- paste("Input",1:3,sep="") stopifnot( names(NodeParents(B))[2]=="Input2" ) ## Detach the parent NodeParents(B)$Input2 <- list(NULL) stopifnot( length(NodeParents(B))==3, NodeKind(NodeParents(B)$Input2) == "Stub" ) ## Remove all parents NodeParents(B) <- list() stopifnot( length(NodeParents(B))==0 ) DeleteNetwork(abnet) stopSession(sess)
sess <- NeticaSession() startSession(sess) abnet <- CreateNetwork("AB", session=sess) anodes <- NewDiscreteNode(abnet, paste("A",1:3,sep="")) B <- NewDiscreteNode(abnet,"B") ## Should be empty list stopifnot(length(NodeParents(B))==0) NodeParents(B) <- anodes stopifnot( length(NodeParents(B))==3, NodeParents(B)[[2]] == anodes[[2]] ) ## Reorder nodes NodeParents(B) <- anodes[c(2:3,1)] stopifnot( length(NodeParents(B))==3, NodeName(NodeParents(B)[[2]])=="A3", all(nchar(names(NodeParents(B)))==0) ) ## Remove a node. NodeParents(B) <- anodes[2:1] stopifnot( length(NodeParents(B))==2, NodeName(NodeParents(B)[[2]])=="A1", all(nchar(names(NodeParents(B)))==0) ) ## Add a node NodeParents(B) <- anodes[3:1] stopifnot( length(NodeParents(B))==3, NodeName(NodeParents(B)[[3]])=="A1", all(nchar(names(NodeParents(B)))==0) ) ##Name the inputs NodeInputNames(B) <- paste("Input",1:3,sep="") stopifnot( names(NodeParents(B))[2]=="Input2" ) ## Detach the parent NodeParents(B)$Input2 <- list(NULL) stopifnot( length(NodeParents(B))==3, NodeKind(NodeParents(B)$Input2) == "Stub" ) ## Remove all parents NodeParents(B) <- list() stopifnot( length(NodeParents(B))==0 ) DeleteNetwork(abnet) stopSession(sess)
A complete Bayesian networks defines a conditional probability distribution for a node given its parents. If all the nodes are discrete, this comes in the form of a conditional probability table a multidimensional array whose first several dimensions follow the parent variable and whose last dimension follows the child variable.
NodeProbs(node) NodeProbs(node) <- value
NodeProbs(node) NodeProbs(node) <- value
node |
An active, discrete |
value |
The new conditional probability table. See details for the expected dimensions. |
Let node
be the node of interest and parent1
,
parent2
, ..., parentp
, where is
the number of parents. Let
pdim =
sapply(NodeParents(node),
NodeNumStates)
be a vector with the number of states for each parent.
A parent configuration is defined by assigning each of the parent
values to one of its possible states. Each parent configuration
defines a (conditional) probability distribution over the possible
states of node.
The result of NodeProbs(node)
will be an array with dimensions
c(pdim, NodeNumStates(node))
. The first
dimensions will be named according to the
NodeInputNames(node)
or the
NodeName(parent)
if the input names are not set. The
last dimension will be named according to the node itself. The
dimnames
for the resulting array will correspond to the state
names.
In the CPTtools
package, this known as the
CPA
format, and tools exist to convert between
this form an a two dimensional matrix, or CPF
format.
The setter form expects an array of the same dimensions as an argument, although it does not need to have the dimnames set.
A conditional probability array of class
c("CPA","array")
. See details.
Note that the expression node[...]
also accesses the partial or
complete node conditional probability table. See
Extract.NeticaNode
.
All of this assumes that these are discrete nodes, that is
is.discrete(node)
will return true for both node
and all of the parents. If the nodes are continuous, they need to
be discritized through the use of NodeLevels
.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNodeProbs_bn(), SetNodeProbs_bn()
Extract.NeticaNode
,
NeticaNode
, NodeParents()
,
NodeInputNames()
, NodeStates()
,
CPA
,
CPF
, normalize()
sess <- NeticaSession() startSession(sess) abc <- CreateNetwork("ABC", session=sess) A <- NewDiscreteNode(abc,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc,"C",c("C1","C2")) AddLink(A,B) AddLink(A,C) AddLink(B,C) NodeProbs(A)<-c(.1,.2,.3,.4) NodeProbs(B) <- normalize(matrix(1:12,4,3)) NodeProbs(C) <- normalize(array(1:24,c(4,3,2))) Aprobs <- NodeProbs(A) Bprobs <- NodeProbs(B) Cprobs <- NodeProbs(C) stopifnot( CPTtools::is.CPA(Aprobs), CPTtools::is.CPA(Bprobs), CPTtools::is.CPA(Cprobs) ) DeleteNetwork(abc) stopSession(sess)
sess <- NeticaSession() startSession(sess) abc <- CreateNetwork("ABC", session=sess) A <- NewDiscreteNode(abc,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc,"C",c("C1","C2")) AddLink(A,B) AddLink(A,C) AddLink(B,C) NodeProbs(A)<-c(.1,.2,.3,.4) NodeProbs(B) <- normalize(matrix(1:12,4,3)) NodeProbs(C) <- normalize(array(1:24,c(4,3,2))) Aprobs <- NodeProbs(A) Bprobs <- NodeProbs(B) Cprobs <- NodeProbs(C) stopifnot( CPTtools::is.CPA(Aprobs), CPTtools::is.CPA(Bprobs), CPTtools::is.CPA(Cprobs) ) DeleteNetwork(abc) stopSession(sess)
A node set is a character label associated with a node which provides information about its role in the models. This function returns or sets the labels associated with a node.
NodeSets(node, incSystem = FALSE) NodeSets(node) <- value AddNodeToSets(node,sets) RemoveNodeFromSets(node,sets)
NodeSets(node, incSystem = FALSE) NodeSets(node) <- value AddNodeToSets(node,sets) RemoveNodeFromSets(node,sets)
node |
An active |
incSystem |
A logical flag. If |
value |
A character vector containing the names of the node sets that
node should be associated with. These names must follow the
|
sets |
A character vector containing the names of the node sets that
node should (or should not) be associated with. These names
must follow the |
Netica node sets are a collection of string labels that can be
associated with various nodes in a network. Node sets do not have any
meaning to Netica: node set membership only affect the way the node
is displayed (see NetworkNodeSetColor()
). One
purpose of node sets is to label a set of nodes that play a similar
role in the model. For example, "ReportingVariable"
or
"Observable"
.
The expression NodeSets(node)
returns the node sets currently
associated with node. If incSystem=TRUE
, then the
internal Netica system node sets will be included as well. These
begin with a colon (‘:’).
The expression NodeSets(node)<-value
removes any node sets
previously associated with node and adds node to the node sets
named in value. The elements of value need not
correspond to existing node sets, new node sets will be created for
new values. (Warning: this implies that if the name of the node set
is spelled incorrectly in one of the calls, this will create a new
node set. For example, "Observable"
and "Observables"
would be two distinct node sets.) Setting the node set associated
with a node only affects user-defined node sets, the Netica system
node sets cannot be set using NodeSet
.
The functions AddNodeToSets
and RemoveNodeFromSets
operate on the current node set in the expected way.
A character vector giving the names of the node sets node is
associated with. The setter form, AddNodeToSets
and
RemoveNodeFromSets
return node.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: AddNodeToNodeset_bn(), RemoveNodeFromNodeset_bn(), IsNodeInNodeset_bn()
NeticaNode
, NodeKind()
,
NetworkNodeSets()
, NetworkSetPriority()
,
NetworkNodesInSet()
, NetworkNodeSetColor()
,
is.IDname()
sess <- NeticaSession() startSession(sess) nsnet <- CreateNetwork("NodeSetExample", session=sess) Ability <- NewContinuousNode(nsnet,"Ability") EssayScore <- NewDiscreteNode(nsnet,"EssayScore",paste("level",5:0,sep="_")) Value <- NewContinuousNode(nsnet,"Value") NodeKind(Value) <- "Utility" Placement <- NewDiscreteNode(nsnet,"Placement", c("Advanced","Regular","Remedial")) NodeKind(Placement) <- "Decision" stopifnot( length(NodeSets(Ability)) == 0, ## Nothing set yet setequal(NodeSets(Ability,TRUE), c(":Continuous", ":Nature", ":TableIncomplete", ":Parentless", ":Childless", ":Node")), !is.na(match(":Utility",NodeSets(Value,TRUE))), !is.na(match(":Decision",NodeSets(Placement,TRUE))) ) NodeSets(Ability) <- "ReportingVariable" stopifnot( NodeSets(Ability) == "ReportingVariable" ) NodeSets(EssayScore) <- "Observable" stopifnot( NodeSets(EssayScore) == "Observable" ) ## Make EssayScore a reporting variable, too NodeSets(EssayScore) <- c("ReportingVariable",NodeSets(EssayScore)) stopifnot( setequal(NodeSets(EssayScore),c("Observable","ReportingVariable")) ) ## Clear out the node set NodeSets(Ability) <- character() stopifnot( length(NodeSets(Ability)) == 0 ) NodeSets(Ability) <- c("Set1","Set2") AddNodeToSets(Ability,c("Set2","Set3")) stopifnot( length(NodeSets(Ability)) == 3, setequal(NodeSets(Ability),c("Set1","Set2","Set3")) ) RemoveNodeFromSets(Ability,c("Set1","Set4")) stopifnot( length(NodeSets(Ability)) == 2, setequal(NodeSets(Ability),c("Set2","Set3")) ) DeleteNetwork(nsnet) stopSession(sess)
sess <- NeticaSession() startSession(sess) nsnet <- CreateNetwork("NodeSetExample", session=sess) Ability <- NewContinuousNode(nsnet,"Ability") EssayScore <- NewDiscreteNode(nsnet,"EssayScore",paste("level",5:0,sep="_")) Value <- NewContinuousNode(nsnet,"Value") NodeKind(Value) <- "Utility" Placement <- NewDiscreteNode(nsnet,"Placement", c("Advanced","Regular","Remedial")) NodeKind(Placement) <- "Decision" stopifnot( length(NodeSets(Ability)) == 0, ## Nothing set yet setequal(NodeSets(Ability,TRUE), c(":Continuous", ":Nature", ":TableIncomplete", ":Parentless", ":Childless", ":Node")), !is.na(match(":Utility",NodeSets(Value,TRUE))), !is.na(match(":Decision",NodeSets(Placement,TRUE))) ) NodeSets(Ability) <- "ReportingVariable" stopifnot( NodeSets(Ability) == "ReportingVariable" ) NodeSets(EssayScore) <- "Observable" stopifnot( NodeSets(EssayScore) == "Observable" ) ## Make EssayScore a reporting variable, too NodeSets(EssayScore) <- c("ReportingVariable",NodeSets(EssayScore)) stopifnot( setequal(NodeSets(EssayScore),c("Observable","ReportingVariable")) ) ## Clear out the node set NodeSets(Ability) <- character() stopifnot( length(NodeSets(Ability)) == 0 ) NodeSets(Ability) <- c("Set1","Set2") AddNodeToSets(Ability,c("Set2","Set3")) stopifnot( length(NodeSets(Ability)) == 3, setequal(NodeSets(Ability),c("Set1","Set2","Set3")) ) RemoveNodeFromSets(Ability,c("Set1","Set4")) stopifnot( length(NodeSets(Ability)) == 2, setequal(NodeSets(Ability),c("Set2","Set3")) ) DeleteNetwork(nsnet) stopSession(sess)
This function returns a list associated with a Netica node. The
function NodeNumStates()
returns the number of states,
NodeStates
returns or manipulates them.
NodeStates(node) NodeNumStates(node) NodeStates(node, resize=FALSE) <- value
NodeStates(node) NodeNumStates(node) NodeStates(node, resize=FALSE) <- value
node |
An active |
value |
A character vector of length |
resize |
A logical scalar. If true, the number of states of the node will be
adjusted to the length of |
States behave slightly differently for discrete and continuous nodes
(see is.discrete()
. For discrete nodes, the random variable
represented by the node can take on one of the values represented by
NodeStates(node)
.
Discrete. The number of states for a discrete node is
determined when the node is created (through a call to
NewDiscreteNode()
). By default, setting the node states
will not change the number of states in the node.
The states are important when building conditional probability
tables (CPTs). In particular, the state names are used to label the columns
of the CPT. Thus, state names can be used to address arrays in the
same way that dimnames
can. In particular, the
state names can be used to index the vectors returned by
NodeStates()
, NodeStateTitles()
,
NodeStateTitles()
, and NodeLevels()
(for
discrete nodes).
Calling NodeStates(node,resize=TRUE) <- value
will
adjust the number of states in the node to match the length of
value. Note that this is a somewhat dangerous operation. If
there is a CPT associated with the node, Netica will adjust it to the
right size using an operation which has not been documented, but seems
like a sensible default. If there is a finding associated with the
node, Netica may raise an error if this state is deleted (RNetica
simply deletes the unneeded states from the end of the list). It is
probably safe to resize the node only in early stages of development.
This is why the default is to raise an error.
Continuous. States for a continuous node are determined by the
NodeLevels()
of the node, which describe a series of
endpoints for intervals that cut the continuous space into the
states. The function NodeNumStates(node)
should return
length(NodeLevels(node))-1
unless the levels have not been set
in which case it will be zero. If NodeStates
are set for a
continuous node, they must have length
length(NodeLevels(node))-1
.
The function NodeNumStates()
returns an integer giving the
number of states.
The function NodeStates()
returns a character vector of length
NodeNumStates(node)
whose values and names are both set to the
state names. The setter version of this function invisibly returns the
node object.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNodeNumberStates_bn(), GetNodeStateName_bn(), SetNodeStateNames_bn(), GetNodeLevels_bn() SetNodeLevels_bn(), AddNodeStates_bn(), RemoveNodeState_bn()
NewDiscreteNode()
, NeticaNode
,
NodeName()
, is.discrete()
,
is.active()
, NodeStateTitles()
,
NodeLevels()
, NodeStateComments()
,
sess <- NeticaSession() startSession(sess) anet <- CreateNetwork("Annette", session=sess) ## Discrete Nodes nodel2 <- NewDiscreteNode(anet,"TwoLevelNode") stopifnot( NodeNumStates(nodel2)==2, NodeStates(nodel2)==c("Yes","No") ) NodeStates(nodel2) <- c("True","False") stopifnot( NodeStates(nodel2)==c("True","False") ) nodel3 <- NewDiscreteNode(anet,"ThreeLevelNode",c("High","Med","Low")) stopifnot( NodeNumStates(nodel3)==3, NodeStates(nodel3)==c("High","Med","Low"), NodeStates(nodel3)[2]=="Med" ) NodeStates(nodel3)[2] <- "Median" stopifnot( NodeStates(nodel3)[2]=="Median" ) NodeStates(nodel3)["Median"] <- "Medium" stopifnot( NodeStates(nodel3)[2]=="Medium" ) ## Adjusting size ## Not run: ## Don't run this it will generate an error. NodeStates(nodel2) <- c("Low","Medium","High") ## End(Not run) ## Should work if we pass resize=TRUE NodeStates(nodel2,resize=TRUE) <- c("Low","Med","High") NodeStates(nodel3,resize=TRUE) <- c("Low","High") stopifnot( NodeNumStates(nodel2)==3, NodeStates(nodel2)==c("Low","Med","High"), NodeNumStates(nodel3)==2, NodeStates(nodel3)==c("Low","High") ) ## Continuous Nodes wnode <- NewContinuousNode(anet,"Weight") ## Not run: ## Don't run this until the levels for wnode have been set, ## it will generate an error. NodeStates(wnode) <- c("Low","Medium","High") ## End(Not run) ## First set levels of node. NodeLevels(wnode) <- c(0, 0.1, 10, Inf) ## Then can set States. NodeStates(wnode) <- c("Low","Medium","High") DeleteNetwork(anet) stopSession(sess)
sess <- NeticaSession() startSession(sess) anet <- CreateNetwork("Annette", session=sess) ## Discrete Nodes nodel2 <- NewDiscreteNode(anet,"TwoLevelNode") stopifnot( NodeNumStates(nodel2)==2, NodeStates(nodel2)==c("Yes","No") ) NodeStates(nodel2) <- c("True","False") stopifnot( NodeStates(nodel2)==c("True","False") ) nodel3 <- NewDiscreteNode(anet,"ThreeLevelNode",c("High","Med","Low")) stopifnot( NodeNumStates(nodel3)==3, NodeStates(nodel3)==c("High","Med","Low"), NodeStates(nodel3)[2]=="Med" ) NodeStates(nodel3)[2] <- "Median" stopifnot( NodeStates(nodel3)[2]=="Median" ) NodeStates(nodel3)["Median"] <- "Medium" stopifnot( NodeStates(nodel3)[2]=="Medium" ) ## Adjusting size ## Not run: ## Don't run this it will generate an error. NodeStates(nodel2) <- c("Low","Medium","High") ## End(Not run) ## Should work if we pass resize=TRUE NodeStates(nodel2,resize=TRUE) <- c("Low","Med","High") NodeStates(nodel3,resize=TRUE) <- c("Low","High") stopifnot( NodeNumStates(nodel2)==3, NodeStates(nodel2)==c("Low","Med","High"), NodeNumStates(nodel3)==2, NodeStates(nodel3)==c("Low","High") ) ## Continuous Nodes wnode <- NewContinuousNode(anet,"Weight") ## Not run: ## Don't run this until the levels for wnode have been set, ## it will generate an error. NodeStates(wnode) <- c("Low","Medium","High") ## End(Not run) ## First set levels of node. NodeLevels(wnode) <- c(0, 0.1, 10, Inf) ## Then can set States. NodeStates(wnode) <- c("Low","Medium","High") DeleteNetwork(anet) stopSession(sess)
Each state of a NeticaNode
can have a longer title or
comments associated with it. These functions get or set the titles
or comments.
NodeStateTitles(node) NodeStateTitles(node) <- value NodeStateComments(node) NodeStateComments(node) <- value
NodeStateTitles(node) NodeStateTitles(node) <- value NodeStateComments(node) NodeStateComments(node) <- value
node |
An active |
value |
A character vector of length |
The titles are meant to be a more human readable version of the state
names and are not subject the IDname
restrictions.
These are displayed in the Netica GUI in certain display modes. The
comments are meant to be a longer free form notes.
Both titles and comments are returned as a named character vector with names corresponding to the state names. Therefore one can change a single state title or comment by accessing it either using the state number or the state name.
Both NodeStateTitles()
and NodeStateComments()
return a
character vector of length NodeNumStates(node)
giving the
titles or comments respectively. The names of this vector are
NodeStates(node)
.
The setter methods return the modified NeticaNode
object invisibly.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNodeStateTitle_bn(),SetNodeStateTitle_bn(), GetNodeStateComment_bn(),SetNodeStateComment_bn()
NeticaNode
, NodeStates()
,
NodeLevels()
sess <- NeticaSession() startSession(sess) cnet <- CreateNetwork("CreativeNet", session=sess) orig <- NewDiscreteNode(cnet,"Originality", c("H","M","L")) NodeStateTitles(orig) <- c("High","Medium","Low") NodeStateComments(orig)[1] <- "Produces solutions unlike those typically seen." stopifnot( NodeStateTitles(orig) == c("High","Medium","Low"), grep("solutions unlike", NodeStateComments(orig))==1, NodeStateComments(orig)[3]=="" ) sol <- NewDiscreteNode(cnet,"Solution", c("Typical","Unusual","VeryUnusual")) stopifnot( all(NodeStateTitles(sol) == ""), all(NodeStateComments(sol) == "") ) NodeStateTitles(sol)["VeryUnusual"] <- "Very Unusual" NodeStateComments(sol) <- paste("Distance from typical solution", c("<1", "1--2", ">2")) stopifnot( NodeStateTitles(sol)[3]=="Very Unusual", NodeStateComments(sol)[1] == "Distance from typical solution <1" ) DeleteNetwork(cnet) stopSession(sess)
sess <- NeticaSession() startSession(sess) cnet <- CreateNetwork("CreativeNet", session=sess) orig <- NewDiscreteNode(cnet,"Originality", c("H","M","L")) NodeStateTitles(orig) <- c("High","Medium","Low") NodeStateComments(orig)[1] <- "Produces solutions unlike those typically seen." stopifnot( NodeStateTitles(orig) == c("High","Medium","Low"), grep("solutions unlike", NodeStateComments(orig))==1, NodeStateComments(orig)[3]=="" ) sol <- NewDiscreteNode(cnet,"Solution", c("Typical","Unusual","VeryUnusual")) stopifnot( all(NodeStateTitles(sol) == ""), all(NodeStateComments(sol) == "") ) NodeStateTitles(sol)["VeryUnusual"] <- "Very Unusual" NodeStateComments(sol) <- paste("Distance from typical solution", c("<1", "1--2", ">2")) stopifnot( NodeStateTitles(sol)[3]=="Very Unusual", NodeStateComments(sol)[1] == "Distance from typical solution <1" ) DeleteNetwork(cnet) stopSession(sess)
The title is a longer name for a node which is not subject to the
Netica IDname
restrictions. The description is a free form
text associated with a node.
NodeTitle(node) NodeTitle(node) <- value NodeDescription(node) NodeDescription(node) <- value
NodeTitle(node) NodeTitle(node) <- value NodeDescription(node) NodeDescription(node) <- value
node |
A |
value |
A character object giving the new title or description. |
The title is meant to be a human readable alternative to the name,
which is not limited to the IDname
restrictions. The
title also affects how the node is displayed in the Netica GUI.
The description is any text the user chooses to attach to the node. If value has length greater than 1, the vector is collapsed into a long string with newlines separating the components.
A character vector of length 1 providing the title or description.
Node descriptions are called "Descriptions" in the Netica GUI, but "Comments" in the API.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNodeTitle_bn(), SetNodeTitle_bn(), GetNodeComments_bn(), SetNodeComments_bn()
sess <- NeticaSession() startSession(sess) net2 <- CreateNetwork("secondNet", session=sess) firstNode <- NewDiscreteNode(net2,"firstNode") NodeTitle(firstNode) <- "My First Bayesian Network Node" stopifnot(NodeTitle(firstNode)=="My First Bayesian Network Node") now <- date() NodeDescription(firstNode)<-c("Node created on",now) stopifnot(NodeDescription(firstNode) == paste(c("Node created on",now),collapse="\n")) ## Print here escapes the newline, so is harder to read cat(NodeDescription(firstNode),"\n") DeleteNetwork(net2) stopSession(sess)
sess <- NeticaSession() startSession(sess) net2 <- CreateNetwork("secondNet", session=sess) firstNode <- NewDiscreteNode(net2,"firstNode") NodeTitle(firstNode) <- "My First Bayesian Network Node" stopifnot(NodeTitle(firstNode)=="My First Bayesian Network Node") now <- date() NodeDescription(firstNode)<-c("Node created on",now) stopifnot(NodeDescription(firstNode) == paste(c("Node created on",now),collapse="\n")) ## Print here escapes the newline, so is harder to read cat(NodeDescription(firstNode),"\n") DeleteNetwork(net2) stopSession(sess)
Netica provides a mechanism for associating user defined values with a
node as a series of key/value pairs. The key must be a
IDname
and the value can be an arbitrary string. The
function NodeUserField
accesses the string value associated
with the key, and the function NodeUserObj
accesses an R object
associated with the key.
NodeUserField(node, fieldname) NodeUserField(node, fieldname) <- value NodeUserObj(node, fieldname) NodeUserObj(node, fieldname) <- value NodeAllUserFields(node)
NodeUserField(node, fieldname) NodeUserField(node, fieldname) <- value NodeUserObj(node, fieldname) NodeUserObj(node, fieldname) <- value NodeAllUserFields(node)
node |
A |
fieldname |
A character scalar conforming to the |
value |
For |
Netica contains a mechanism for associating user data with nodes.
In the Netica documentation, they note that only strings are really
supported as only strings are portable across implementations.The
function NodeUserField
provides direct access for storing
strings.
The function NodeUserObj
wraps the call to
NodeUserField
with a call to dputToString
or
dgetFromString
to allow the serialization of arbitrary
objects.
The function NodeUserField
returns a character scalar with
the value stored in the field fieldname, or NA
if no
such field exists.
The function NodeUserObj
returns an arbitrary object created
by calling dgetFromString
on the value stored in the
field fieldname, or NULL
if no such field exists. If
the string cannot be interpreted as an R object, it generates an
error.
The function NodeAllUserFields
returns a character vector
containing all user data stored with the node (this will be the
serialized versions of the objects, not the objects themselves). The
names of the result are the names of the fields.
In his book Extending R John Chambers suggest serializing R objects through XML or JSON mechanisms rather than the older dump protocol. I may move to that later, although it will likely cause backwards compatability issues.
Russell Almond
http://norsys.com/onLineAPIManual/index.html GetNodeUserField_bn(), SetNodeUserField_bn(), GetNodeNthUserField_bn()
NeticaNode
, NodeDescription()
NetworkUserField
, dputToString()
sess <- NeticaSession() startSession(sess) usedNet <- CreateNetwork("UsedNet", session=sess) userNode <- NewContinuousNode(usedNet, "UserNode") NodeUserField(userNode,"Author") <- "Russell Almond" NodeUserField(userNode,"Status") <- "In Progress" stopifnot(NodeUserField(userNode,"Author")=="Russell Almond") stopifnot(NodeUserField(userNode,"Status")=="In Progress") fields <- NodeAllUserFields(userNode) stopifnot(length(fields)==2) stopifnot(all(!is.na(match(c("Russell Almond","In Progress"),fields)))) stopifnot(all(!is.na(match(c("Author","Status"),names(fields))))) stopifnot(is.na(NodeUserField(userNode,"gender"))) stopifnot(is.null(NodeUserObj(userNode,"gender"))) x <- sample(1L:10L) NodeUserObj(userNode,"x") <- x x1 <- NodeUserObj(userNode,"x") stopifnot(all(x==x1)) DeleteNetwork(usedNet) stopSession(sess)
sess <- NeticaSession() startSession(sess) usedNet <- CreateNetwork("UsedNet", session=sess) userNode <- NewContinuousNode(usedNet, "UserNode") NodeUserField(userNode,"Author") <- "Russell Almond" NodeUserField(userNode,"Status") <- "In Progress" stopifnot(NodeUserField(userNode,"Author")=="Russell Almond") stopifnot(NodeUserField(userNode,"Status")=="In Progress") fields <- NodeAllUserFields(userNode) stopifnot(length(fields)==2) stopifnot(all(!is.na(match(c("Russell Almond","In Progress"),fields)))) stopifnot(all(!is.na(match(c("Author","Status"),names(fields))))) stopifnot(is.na(NodeUserField(userNode,"gender"))) stopifnot(is.null(NodeUserObj(userNode,"gender"))) x <- sample(1L:10L) NodeUserObj(userNode,"x") <- x x1 <- NodeUserObj(userNode,"x") stopifnot(all(x==x1)) DeleteNetwork(usedNet) stopSession(sess)
This enters a numeric value (finding) for a continuous node or a discrete node which has numeric values assigned to the states.
NodeValue(node) NodeValue(node) <- value
NodeValue(node) NodeValue(node) <- value
node |
An active |
value |
A real value for the node. |
The behavior of the levels depends on whether the node is discrete
(is.discrete(node)==TRUE
) or continuous
(is.discrete(node)==FALSE
).
Discrete. For if node is a discrete node, then the
states of node must have been assigned numeric values with
NodeLevels(node)
in order for NodeValue()
to make sense. If all states have not been assigned values,
NodeValue()
will generate an error.
If the value of the node is determined, either because the value of
the node has been set with NodeFinding()
or because the
value can be determined exactly from the value of other nodes in the
network (through logical probability distributions or formulae), then
NodeValue(node)
will return the value associated with the
state of the node. Otherwise, it will return NA
.
The expression NodeValue(node)<-value
, can be used to
set the value of node to the state associate with the numerical
value. If value does not correspond to one of the node
levels, this will generate an error.
Continuous. For continuous nodes,
NodeFinding(node)
returns the value associated with the
node, either set with a previous call to
NodeValue(node)<-value
, or which can be determined
through formulae.
The expression NodeValue(node) <- value
will set the
value (the equivalent of a finding for a discrete node) to
value. If node has been associated with cut
scores through a previous call to NodeLevels(node)
, then
this will also associate a finding with the node.
The function NodeValue(node)
returns the value of node
if that can be determined (see Details) or NA
if it cannot. It
may generate an error if node
is discrete and has not had
numeric values associated with its states.
The expression NodeValue(node) <- value
returns
node
.
Netica manual is not particularly clear on how continuous nodes are handled.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNodeValueEntered_bn(), EnterNodeValue_bn()
NodeFinding()
, NodeLevels()
,
EnterNegativeFinding()
, EnterFindings()
,
RetractNodeFinding()
, NodeStates()
,
NodeEquation()
,is.continuous()
,
NodeExpectedValue()
, NodeBeliefs()
sess <- NeticaSession() startSession(sess) aNet <- CreateNetwork("aNet", session=sess) dTheta <- NewDiscreteNode(aNet, "ThetaD", c("neg2","neg1","zero","pos1","pos2")) NodeLevels(dTheta) <- c(-2,-1,0,1,2) NodeFinding(dTheta) <- "pos1" stopifnot(NodeValue(dTheta)==1) NodeValue(dTheta) <- 0 stopifnot(NodeFinding(dTheta)=="zero") ## Not run: ## The error handling seems broken under Windows. cat("This next statement generates an error as 1/2 is not a legal value.") stopifnot(class(try(NodeValue(dTheta) <- 1/2)) == "try-error") ## End(Not run) cTheta <- NewContinuousNode(aNet, "ThetaC") NodeLevels(cTheta) <- qnorm(c(.001,1/5,2/5,2/5,4/5,.999)) ## Netica doesn't allow - sign or decimal point in state name, need to ## jump through a few hoops here. midpoints <- round(qnorm((1:5)/5-.1),2) NodeStates(cTheta) <- sub(".","o", paste(ifelse(midpoints<0,"n","p"),abs(midpoints),sep=""), fixed=TRUE) NodeValue(cTheta) <- -1 stopifnot(NodeFinding(cTheta)=="n1o28") NodeFinding(cTheta) <- "p0" ## No value associated with this finding. stopifnot(is.na(NodeValue(cTheta))) DeleteNetwork(aNet) stopSession(sess)
sess <- NeticaSession() startSession(sess) aNet <- CreateNetwork("aNet", session=sess) dTheta <- NewDiscreteNode(aNet, "ThetaD", c("neg2","neg1","zero","pos1","pos2")) NodeLevels(dTheta) <- c(-2,-1,0,1,2) NodeFinding(dTheta) <- "pos1" stopifnot(NodeValue(dTheta)==1) NodeValue(dTheta) <- 0 stopifnot(NodeFinding(dTheta)=="zero") ## Not run: ## The error handling seems broken under Windows. cat("This next statement generates an error as 1/2 is not a legal value.") stopifnot(class(try(NodeValue(dTheta) <- 1/2)) == "try-error") ## End(Not run) cTheta <- NewContinuousNode(aNet, "ThetaC") NodeLevels(cTheta) <- qnorm(c(.001,1/5,2/5,2/5,4/5,.999)) ## Netica doesn't allow - sign or decimal point in state name, need to ## jump through a few hoops here. midpoints <- round(qnorm((1:5)/5-.1),2) NodeStates(cTheta) <- sub(".","o", paste(ifelse(midpoints<0,"n","p"),abs(midpoints),sep=""), fixed=TRUE) NodeValue(cTheta) <- -1 stopifnot(NodeFinding(cTheta)=="n1o28") NodeFinding(cTheta) <- "p0" ## No value associated with this finding. stopifnot(is.na(NodeValue(cTheta))) DeleteNetwork(aNet) stopSession(sess)
When displayed in the GUI, Netica nodes have a position. The
NodeVisPos()
attribute controls where the node will be
displayed.
NodeVisPos(node) NodeVisPos(node) <- value
NodeVisPos(node) NodeVisPos(node) <- value
node |
A |
value |
A numeric vector of length 2 giving the |
The visual position of the node doesn't make much different in RNetica, as R does not display the node. However, it will control the appearance when the net is loaded into the Netica GUI.
A numeric vector of length 2 with names "x"
and "y"
.
The minimum possible node position appears to be (0,0) and the maximum is never stated. Netica appears to round positions to the nearest integer. Also, if the position appears too close to the boarder (Netica positions the center of the node), Netica will move it away from the edge.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNodeVisPosition_bn(), SetNodeVisPosition_bn(),
sess <- NeticaSession() startSession(sess) pnet <- CreateNetwork("PositionNet", session=sess) pnode <- NewDiscreteNode(pnet,"PlaceMe") NodeVisPos(pnode) <- c(100,300) pos <- NodeVisPos(pnode) stopifnot( pos["x"] ==100, pos["y"] ==300 ) ## Netica rounds noninteger positions. NodeVisPos(pnode) <- c(74.3,88.8) pos <- NodeVisPos(pnode) stopifnot( pos["x"] ==74, pos["y"] ==88 ) ## Warning, setting a node too close to the edge can cause Netica to ## reposition the node NodeVisPos(pnode) <- c(1,1) pos <- NodeVisPos(pnode) stopifnot( pos["x"] >1, pos["y"] >1 ) DeleteNetwork(pnet) stopSession(sess)
sess <- NeticaSession() startSession(sess) pnet <- CreateNetwork("PositionNet", session=sess) pnode <- NewDiscreteNode(pnet,"PlaceMe") NodeVisPos(pnode) <- c(100,300) pos <- NodeVisPos(pnode) stopifnot( pos["x"] ==100, pos["y"] ==300 ) ## Netica rounds noninteger positions. NodeVisPos(pnode) <- c(74.3,88.8) pos <- NodeVisPos(pnode) stopifnot( pos["x"] ==74, pos["y"] ==88 ) ## Warning, setting a node too close to the edge can cause Netica to ## reposition the node NodeVisPos(pnode) <- c(1,1) pos <- NodeVisPos(pnode) stopifnot( pos["x"] >1, pos["y"] >1 ) DeleteNetwork(pnet) stopSession(sess)
Netica internally has a number of styles it can use to draw a node,
these including, "Default", "Absent", "Shape", "LabeledBox",
"BeliefBars", "BeliefLine", and "Meter". The function
NodeVisStyle()
returns how the node will be displayed, or
sets how it will be displayed.
NodeVisStyle(node) NodeVisStyle(node) <- value
NodeVisStyle(node) NodeVisStyle(node) <- value
node |
A |
value |
A character string giving the new style. Must be one of
|
The visual style of the node doesn't make much different in RNetica, as R does not display the node. However, it will control the appearance when the node is loaded into the Netica GUI.
A character string which is one of the values "Default", "Absent",
"Shape", "LabeledBox", "BeliefBars", "BeliefLine", or "Meter", or
NA
if an error occurred.
The setter method returns the modified node
object.
The Netica documentation indicates that in the future additional parameters can be added to the style, for example: "LabeledBox,CornerRoundingRadius=3,LineThickness=2"
With Netica API 5.10 (current on Windows but not Unix/Mac OS) the default value seems to be "None" and not "Default".
Russell Almond
http://norsys.com/onLineAPIManual/index.html: GetNodeVisStyle_bn(), SetNodeVisStyle_bn(),
sess <- NeticaSession() startSession(sess) snet <- CreateNetwork("StylishNet", session=sess) snode <- NewDiscreteNode(snet,"StyleMe") stopifnot((NodeVisStyle(snode)=="Default" || NodeVisStyle(snode)=="None")) NodeVisStyle(snode) <- "Meter" stopifnot(NodeVisStyle(snode)=="Meter") DeleteNetwork(snet) stopSession(sess)
sess <- NeticaSession() startSession(sess) snet <- CreateNetwork("StylishNet", session=sess) snode <- NewDiscreteNode(snet,"StyleMe") stopifnot((NodeVisStyle(snode)=="Default" || NodeVisStyle(snode)=="None")) NodeVisStyle(snode) <- "Meter" stopifnot(NodeVisStyle(snode)=="Meter") DeleteNetwork(snet) stopSession(sess)
This function returns a list each of whose elements is a character
vector giving the states of the parent variables (i.e., the result of
calling NodeStates)
on each of the elements of
NodeParents(node)
). The names of this list are the
names assigned to the edges through NodeInputNames(node)
,
or the names of the parent variables if edge names were not supplied.
ParentStates(node) ParentNames(node)
ParentStates(node) ParentNames(node)
node |
An active |
For ParentStates(node)
, named list where each element
corresponds to the states of a parent variable. If node has no
parents, it returns a list of length 0.
The function ParentNames(node)
returns
names(ParentNames(node)
, only is faster.
This is a slightly more sophisticated version of
lapply(NodeParents(node), NodeStates)
. It does minimal
checking so that it can be fast.
Russell Almond
NodeStates()
, NodeParents()
,
NodeInputNames()
sess <- NeticaSession() startSession(sess) abc1 <- CreateNetwork("ABC1", session=sess) A <- NewDiscreteNode(abc1,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc1,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc1,"C",c("C1","C2")) stopifnot( length(ParentStates(A)) == 0 ) AddLink(A,B) Bpars <- ParentStates(B) stopifnot( length(Bpars) == 1, names(Bpars) == "A", Bpars$A==NodeStates(A) ) AddLink(A,C) AddLink(B,C) NodeInputNames(C) <- c("A_type","B_type") Cpars <- ParentStates(C) stopifnot( length(Cpars) == 2, names(Cpars) == c("A_type","B_type"), Cpars[[1]]==NodeStates(A), Cpars$B_type==NodeStates(B) ) DeleteNetwork(abc1) stopSession(sess)
sess <- NeticaSession() startSession(sess) abc1 <- CreateNetwork("ABC1", session=sess) A <- NewDiscreteNode(abc1,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc1,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc1,"C",c("C1","C2")) stopifnot( length(ParentStates(A)) == 0 ) AddLink(A,B) Bpars <- ParentStates(B) stopifnot( length(Bpars) == 1, names(Bpars) == "A", Bpars$A==NodeStates(A) ) AddLink(A,C) AddLink(B,C) NodeInputNames(C) <- c("A_type","B_type") Cpars <- ParentStates(C) stopifnot( length(Cpars) == 2, names(Cpars) == c("A_type","B_type"), Cpars[[1]]==NodeStates(A), Cpars$B_type==NodeStates(B) ) DeleteNetwork(abc1) stopSession(sess)
This function reads a row from a Netica case stream and instantiates the values of the listed nodes to the values found in that row of the case stream.
ReadFindings(nodes, stream, pos = "NEXT", add = FALSE)
ReadFindings(nodes, stream, pos = "NEXT", add = FALSE)
nodes |
The a list of active |
stream |
A |
pos |
A character or integer scalar. This should almost
certainly be one of the two string values “FIRST” or
“NEXT”. It also can be an integer giving the position (in
characters) where to start the machine. This is likely to produce
surprising results unless the integer value is a value obtained from
calling |
add |
A logical scalar. If true, the findings from the case stream are added to the existing node. If false, they are ignored. |
A case file is a table where the rows represent cases, and the columns
represent variables. ReadFindings
reads a row out the
table and instantiates (NodeFinding
) the nodes in
nodeset
to those values. If a the value corresponding a node
is the value of CaseFileMissingCode()
, then it is not
instantiated. The values in the columns are separated by the value of
CaseFileDelimiter()
.
If add
is false, it will first retract any findings associated
with the nodes in nodeset
. If a finding is associated with a
node, the the case file would cause it to be set to an inconsistent
value, then an error will be generated.
The argument pos
determines which record will be read next. If
the value is "NEXT"
the next code will be read. If the value
is "FIRST"
the first code will be read. If the value is a
positive integer, then the record which starts at that character will
be read. On completion of the read, the value of
getCaseStreamPos(stream)
is set to the starting position
of the last read stream. This is also true when
WriteFindings
is called. It is almost certainly an
error to set the pos
argument to anything but either one of the
special string constants or a value which was previously cached after
calling getCaseStreamPos
. If the case stream is at the end,
then getCaseStreamPos(stream)
will be set to NA
.
There are two special columns in the file. The column “IDnum”
contains ID numbers for the cases. The value of
getCaseStreamLastId(stream)
is set to the value of this
column if it is present in the case stream, otherwise it will be set
to -1
. The value of the column
“NumCases” contains a weight to give to the current row. The
value of getCaseStreamLastFreq(stream)
is set to this
value, if it is present. The returned stream object will have these
updated properties, otherwise it will be set
to -1
.
Returns the caseOrStream argument invisibly. Note that the
values of getCaseStreamPos(stream)
will return the
position of the next record or NA
if there are no records left
in the stream. The values of
getCaseStreamLastId(stream)
, and
getCaseStreamLastFreq(stream)
will be updated to reflect
the values from the last read record, or will be -1
if these
values are not provided in the stream.
The first time that ReadFindings
is called on a stream it must
be called with pos="FIRST"
. Failing to do so produces a fatal
error in Netica.
The value of case_posn
returned by the Netica
ReadNetFindings2_bn
function (which is the value to which
getCaseStreamPos(stream)
) is undocumented. I confirmed
with Brent that this is in fact the position in characters from the
start of the stream to the record. It is not recommended, however,
that program rely on that fact.
The fact that the case positions are difficult to compute makes random
access difficult. If it is needed, programmers will need to save the
values of getCaseStreamPos
on previous calls to
ReadFindings
or WriteFindings
. Fetching cases by
the ID requires scanning through the case file (see
WithOpenCaseStream
for an example).
Russell G. Almond
http://norsys.com/onLineAPIManual/index.html: ReadNetFindings2_bn()
CaseFileDelimiter
, CaseFileMissingCode
,
NodeFinding
, RetractNetFindings
ReadFindings
, CaseStream
,
WithOpenCaseStream
sess <- NeticaSession() startSession(sess) abc <- CreateNetwork("ABC", session=sess) A <- NewDiscreteNode(abc,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc,"C",c("C1","C2")) AddLink(A,B) AddLink(A,C) AddLink(B,C) ## Input filename ## Note, this is a cached copy of the file written in the WriteFindings ## documentation. casefile <- system.file("testData","abctestcases.cas", package="RNetica") filestream <- CaseFileStream(casefile, session=sess) ## Case 1 filestream <- ReadFindings(list(A,B,C),filestream,"FIRST") stopifnot( NodeFinding(A) == "A1", NodeFinding(B) == "B1", NodeFinding(C) == "C1", getCaseStreamLastId(filestream)==1001, abs(getCaseStreamLastFreq(filestream)-1.0) < .0001) pos1 <- getCaseStreamPos(filestream) ## Case 2 filestream <- ReadFindings(list(A,B,C),filestream,"NEXT") stopifnot( NodeFinding(A) == "A2", NodeFinding(B) == "B2", NodeFinding(C) == "C2", getCaseStreamLastId(filestream)==1002, abs(getCaseStreamLastFreq(filestream)-2.0) < .0001) ## Case 3 filestream <- ReadFindings(list(A,B,C),filestream,"NEXT") stopifnot( NodeFinding(A) == "A3", NodeFinding(B) == "B3", NodeFinding(C) == "@NO FINDING", getCaseStreamLastId(filestream)==1003, abs(getCaseStreamLastFreq(filestream)-1.0) < .0001) ## At end of file filestream <- ReadFindings(list(A,B,C),filestream,"NEXT") stopifnot(is.na(getCaseStreamPos(filestream))) ## Restart from Case 1 filestream <- ReadFindings(list(A,B,C),filestream,"FIRST") stopifnot( NodeFinding(A) == "A1", NodeFinding(B) == "B1", NodeFinding(C) == "C1", getCaseStreamLastId(filestream)==1001, abs(getCaseStreamLastFreq(filestream)-1.0) < .0001, pos1 == getCaseStreamPos(filestream)) ## Test with memory stream cases <- read.CaseFile(casefile, session=sess) abcstream <- CaseMemoryStream(cases, session=sess) MemoryStreamContents(abcstream) abcstream <- ReadFindings(list(A,B,C),abcstream,"FIRST") stopifnot( NodeFinding(A) == "A1", NodeFinding(B) == "B1", NodeFinding(C) == "C1", getCaseStreamLastId(abcstream)==1001, abs(getCaseStreamLastFreq(abcstream)-1.0) < .0001) ##Clean Up CloseCaseStream(filestream) CloseCaseStream(abcstream) DeleteNetwork(abc) stopSession(sess)
sess <- NeticaSession() startSession(sess) abc <- CreateNetwork("ABC", session=sess) A <- NewDiscreteNode(abc,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc,"C",c("C1","C2")) AddLink(A,B) AddLink(A,C) AddLink(B,C) ## Input filename ## Note, this is a cached copy of the file written in the WriteFindings ## documentation. casefile <- system.file("testData","abctestcases.cas", package="RNetica") filestream <- CaseFileStream(casefile, session=sess) ## Case 1 filestream <- ReadFindings(list(A,B,C),filestream,"FIRST") stopifnot( NodeFinding(A) == "A1", NodeFinding(B) == "B1", NodeFinding(C) == "C1", getCaseStreamLastId(filestream)==1001, abs(getCaseStreamLastFreq(filestream)-1.0) < .0001) pos1 <- getCaseStreamPos(filestream) ## Case 2 filestream <- ReadFindings(list(A,B,C),filestream,"NEXT") stopifnot( NodeFinding(A) == "A2", NodeFinding(B) == "B2", NodeFinding(C) == "C2", getCaseStreamLastId(filestream)==1002, abs(getCaseStreamLastFreq(filestream)-2.0) < .0001) ## Case 3 filestream <- ReadFindings(list(A,B,C),filestream,"NEXT") stopifnot( NodeFinding(A) == "A3", NodeFinding(B) == "B3", NodeFinding(C) == "@NO FINDING", getCaseStreamLastId(filestream)==1003, abs(getCaseStreamLastFreq(filestream)-1.0) < .0001) ## At end of file filestream <- ReadFindings(list(A,B,C),filestream,"NEXT") stopifnot(is.na(getCaseStreamPos(filestream))) ## Restart from Case 1 filestream <- ReadFindings(list(A,B,C),filestream,"FIRST") stopifnot( NodeFinding(A) == "A1", NodeFinding(B) == "B1", NodeFinding(C) == "C1", getCaseStreamLastId(filestream)==1001, abs(getCaseStreamLastFreq(filestream)-1.0) < .0001, pos1 == getCaseStreamPos(filestream)) ## Test with memory stream cases <- read.CaseFile(casefile, session=sess) abcstream <- CaseMemoryStream(cases, session=sess) MemoryStreamContents(abcstream) abcstream <- ReadFindings(list(A,B,C),abcstream,"FIRST") stopifnot( NodeFinding(A) == "A1", NodeFinding(B) == "B1", NodeFinding(C) == "C1", getCaseStreamLastId(abcstream)==1001, abs(getCaseStreamLastFreq(abcstream)-1.0) < .0001) ##Clean Up CloseCaseStream(filestream) CloseCaseStream(abcstream) DeleteNetwork(abc) stopSession(sess)
The function RetractNodeFinding(node)
clears any findings or
virtual findings set with NodeFinding()
,
EnterNegativeFinding()
or
NodeLikelihood()
and associated with node. The
function RetractNetFindings(net)
clears any findings associated
with any node in the network.
RetractNodeFinding(node) RetractNetFindings(net)
RetractNodeFinding(node) RetractNetFindings(net)
node |
An active |
net |
An active |
This is an undo function for NodeFinding()
,
EnterNegativeFinding()
or
NodeLikelihood()
. In particular, it allows for entering
hypothesized findings for various calculations.
Returns its argument invisibly.
If SetNetworkAutoUpdate()
has been set to TRUE
,
then this function could take some time as each finding is
individually propagated. Consider wrapping multiple calls setting
NodeFinding()
in WithoutAutoUpdate(net, ...)
.
The Netica functions for setting node findings require the programmer
to call RetractNodeFindings_bn()
before setting values to clear
out old findings. The RNetica functions do this internally, so the
user does not need to worry about this.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: RetractNetFindings_bn(), RetractNodeFindings_bn()
NeticaBN
, NeticaNode
,
NodeBeliefs()
,
EnterNegativeFinding()
,
EnterGaussianFinding()
, EnterIntervalFinding()
,
NodeFinding()
, NodeLikelihood()
sess <- NeticaSession() startSession(sess) irt5 <- ReadNetworks(system.file("sampleNets","IRT5.dne", package="RNetica"), session=sess) irt5.theta <- NetworkFindNode(irt5,"Theta") irt5.x <- NetworkFindNode(irt5,paste("Item",1:5,sep="_")) CompileNetwork(irt5) ## Ready to enter findings stopifnot(NodeFinding(irt5.x[[1]]) == "@NO FINDING") NodeFinding(irt5.x[[1]]) <- "Right" stopifnot(NodeFinding(irt5.x[[1]]) == "Right") RetractNodeFinding(irt5.x[[1]]) stopifnot(NodeFinding(irt5.x[[1]]) == "@NO FINDING") NodeFinding(irt5.x[[1]]) <- "Wrong" NodeFinding(irt5.x[[2]]) <- 1 NodeFinding(irt5.x[[3]]) <- 2 stopifnot( NodeFinding(irt5.x[[1]]) == "Wrong", NodeFinding(irt5.x[[2]]) == "Right", NodeFinding(irt5.x[[3]]) == "Wrong", NodeFinding(irt5.x[[4]]) == "@NO FINDING", NodeFinding(irt5.x[[5]]) == "@NO FINDING" ) RetractNetFindings(irt5) stopifnot( NodeFinding(irt5.x[[1]]) == "@NO FINDING", NodeFinding(irt5.x[[2]]) == "@NO FINDING", NodeFinding(irt5.x[[3]]) == "@NO FINDING", NodeFinding(irt5.x[[4]]) == "@NO FINDING", NodeFinding(irt5.x[[5]]) == "@NO FINDING" ) DeleteNetwork(irt5) stopSession(sess)
sess <- NeticaSession() startSession(sess) irt5 <- ReadNetworks(system.file("sampleNets","IRT5.dne", package="RNetica"), session=sess) irt5.theta <- NetworkFindNode(irt5,"Theta") irt5.x <- NetworkFindNode(irt5,paste("Item",1:5,sep="_")) CompileNetwork(irt5) ## Ready to enter findings stopifnot(NodeFinding(irt5.x[[1]]) == "@NO FINDING") NodeFinding(irt5.x[[1]]) <- "Right" stopifnot(NodeFinding(irt5.x[[1]]) == "Right") RetractNodeFinding(irt5.x[[1]]) stopifnot(NodeFinding(irt5.x[[1]]) == "@NO FINDING") NodeFinding(irt5.x[[1]]) <- "Wrong" NodeFinding(irt5.x[[2]]) <- 1 NodeFinding(irt5.x[[3]]) <- 2 stopifnot( NodeFinding(irt5.x[[1]]) == "Wrong", NodeFinding(irt5.x[[2]]) == "Right", NodeFinding(irt5.x[[3]]) == "Wrong", NodeFinding(irt5.x[[4]]) == "@NO FINDING", NodeFinding(irt5.x[[5]]) == "@NO FINDING" ) RetractNetFindings(irt5) stopifnot( NodeFinding(irt5.x[[1]]) == "@NO FINDING", NodeFinding(irt5.x[[2]]) == "@NO FINDING", NodeFinding(irt5.x[[3]]) == "@NO FINDING", NodeFinding(irt5.x[[4]]) == "@NO FINDING", NodeFinding(irt5.x[[5]]) == "@NO FINDING" ) DeleteNetwork(irt5) stopSession(sess)
This reverses the link between parent and child so that it now points from child to parent. If child has additional parents, they are connected to parent and the conditional probability tables are adjusted so that the joint probability distribution across all nodes in the network remains the same.
ReverseLink(parent, child)
ReverseLink(parent, child)
parent |
An active |
child |
An active |
This is not just a simple reversal of a single edge, but rather the
influence diagram operation of arc reversal. Netica will add
additional links to enforce any conditional probability relationship.
For example, Consider a net where A
and B
are both
parents of C
, but A
is not directly connected to
C
. After reversing the arc between B
and C
,
A
will also become a parent of B
to maintain the joint
distribution.
Returns NULL
if successful and NA
if there was a problem.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: ReverseLink_bn()
Shachter, R. D. (1986) "Evaluating Influence Diagrams." Operations Research, 34, 871–82.
NeticaNode
, AddLink()
,
NodeChildren()
, NodeParents()
,
AbsorbNodes()
, is.NodeRelated()
sess <- NeticaSession() startSession(sess) abcnet <- CreateNetwork("ABC", session=sess) A <- NewDiscreteNode(abcnet,"A") B <- NewDiscreteNode(abcnet,"B") C <- NewDiscreteNode(abcnet,"C") AddLink(A,C) AddLink(B,C) stopifnot( is.NodeRelated(A,C,"parent"), is.NodeRelated(C,B,"child"), !is.NodeRelated(A,B,"parent") ) ReverseLink(B,C) stopifnot( is.NodeRelated(A,C,"parent"), is.NodeRelated(C,B,"parent"), is.NodeRelated(A,B,"parent") ) DeleteNetwork(abcnet) stopSession(sess)
sess <- NeticaSession() startSession(sess) abcnet <- CreateNetwork("ABC", session=sess) A <- NewDiscreteNode(abcnet,"A") B <- NewDiscreteNode(abcnet,"B") C <- NewDiscreteNode(abcnet,"C") AddLink(A,C) AddLink(B,C) stopifnot( is.NodeRelated(A,C,"parent"), is.NodeRelated(C,B,"child"), !is.NodeRelated(A,B,"parent") ) ReverseLink(B,C) stopifnot( is.NodeRelated(A,C,"parent"), is.NodeRelated(C,B,"parent"), is.NodeRelated(A,B,"parent") ) DeleteNetwork(abcnet) stopSession(sess)
This function creates (or destroys) a Netica environment. The
StartNetica
function also allows you to set various parameters
associated with the Netica environment.
startSession(session) stopSession(session) restartSession(session) StartNetica(license = options("NeticaLicenseKey")[[1]], checking = NULL, maxmem = NULL, session=NeticaSession(LicenseKey=license, Checking=checking, maxmem=maxmem)) StopNetica(session=getDefaultSession())
startSession(session) stopSession(session) restartSession(session) StartNetica(license = options("NeticaLicenseKey")[[1]], checking = NULL, maxmem = NULL, session=NeticaSession(LicenseKey=license, Checking=checking, maxmem=maxmem)) StopNetica(session=getDefaultSession())
session |
An object of class |
license |
A string containing a license key from Norsys. If this
is |
checking |
A character string containing one of the keywords:
|
maxmem |
An integer containing the maximum amount of memory to be used by the Netica shared library in bytes. If supplied, this should be at least 200,000. |
The generic functions startSession
and stopSession
start
and stop the connection to Netica. Note that the session is active
(is.active
) if the session has been started, and
inactive if it was stopped (or not yet started). The generic function
restartSession
stops the session and starts it again.
The functions StartNetica
and StopNetica
are depricated,
but still included for backwards compatability. The function
StartNetica
will now create a new object of class
NeticaSession
passing its arguments to the
constructor. It will also set a variable DefaultNeticaSession
in the global environment to the new session. As this is the value
returned by getDefaultSession()
this will cause RNetica
to behave like the previous versions where the session pointer was
stored internally to the C code. The function StopNetica
operates on the default session if no explicit argument is given.
Netica is commercial software. The RNetica package downloads and
installs the demonstration version of Netica which is limited in its
functionality (particularly in the size of the networks it handles).
Unlocking the full version of Netica requires a license key which can
be purchased from Norsys (http://www.Norsys.com/). They will
send a license key which unlocks the full capabilities of the shared
library. This can be passed as the first argument to
StartNetica()
. If the value of the first argument is
NULL
then the demonstration version is used instead of the
licensed version (could be useful for testing).
Prior to RNetica version 0.5, RNetica looked for a variable
called NeticaLicenseKey
in the global workspace when
RNetica
is loaded. This then becomes the default value for
both StartNetica
and getDefaultSession()
.
If no value is set for NeticaLicenseKey
, the default value for
license
is set to NULL
, which loads the demo version of
Netica
.
In version 0.5 and later of RNetica, the recommended way to store the
license is to create a default session and store it in the variable
DefaultNeticaSession
in the global environment. This session
object then contains the license key.
Starting with verison 0.9, the new recommended way to set the license
key is options("NeticaLicenseKey")<-
. This should now get
picked up by getDefaultSession
as well as used as a default in
NeticaSession
.
The checking
argument, if supplied, is used to call the Netica
function ArgumentChecking_ns()
. See the documentation of that
function for the meaning of the codes. The default value,
"REGULAR_CHECK"
is appropriate for most development situations.
The maxmem
argument, if supplied, is used to limit the amount
of memory used by Netica. This is passed in a call to the Netica
function LimitMemoryUsage_ns()
. Netica will complain if this
value is less than 200,000. Leaving this as NULL
will not
place limits on the size of Netica's memory for tables and things.
The function StopNetica()
calls the Netica function
CloseNetica_bn()
. It is mainly used when one wants to stop
Netica and restart it with other parameters.
As of RNetica 0.5, the function StartNetica
is no longer called
when the package is attached (in the .onAttach()
function).
Instead, users should start Netica scripts with
startSession(DefaultNeticaSession)
. Note that the pathnames of
recently loaded networks are stored in the session object, so that
networks can be quickly re-read from a saved session.
These functions now all return an object of class
NeticaSession
. Note that StartNetica
sets
the value of DefaultNeticaSession
in the global environment to
the value of the session
argument.
The Netica API is not free-as-in-speech software, the use of the Netica shared library makes you subject to the Netica License agreement (which can be found in the RNetica folder in your R library. If you do not agree to the terms of that license, please uninstall RNetica.
The Netica API is also not free-as-in-beer software. The demonstration version of the Netica API, however, is. In order for you to make full use of the RNetica API, you must purchase a Netica API license from Norsys (http://norsys.com/).
RNetica itself (the glue layers between R and Netica) is free (in both the speech and beer senses) software. Suggestions for improvements and bug fixes are welcome.
Starting with RNetica 0.5, the Netica environment pointer, which is
used by the Netica shared library is stored inside of the
NeticaSession
object instead of as a global object in the C
code. The function is.active()
checks to see whether
that pointer is set (active) or null (inactive). Note that when a
session object is saved, the session pointer is not saved and should
be set to null.
The pre verison 0.5 method used a variable NeticaLicenseKey
in
the global environment to store the license key. The post 0.5 method
uses a variable DefaultNeticaSession
to store a session object
instead, but for backwards compatability, it will try to create a
session using the value of NeticaLicenseKey
if no default
session exists.
Russell Almond
http://norsys.com/onLineAPIManual/index.html: NewNeticaEnviron_ns(), InitNetica2_bn(), CloseNetica_bn(), LimitMemoryUsage_ns(), ArgumentChecking_ns()
NeticaSession
, NeticaSession()
,
NeticaVersion()
, CreateNetwork()
## Not run: ## Recommended way of doing things DefaultNeticaSession <- NeticaSession(LicenseKey="Code from Norsys") startSession(DefaultNeticaSession) ## If DefaultNeticaSession was created in a previous R session ## Re-read the networks. for (netname in DefaultNeticaSession$listNets()) { net <- DefaultNeticaSession$findNet(netname) ReadNetworks(GetNetworkFileName(net),DefaultNeticaSession) } restartSession(DefaultNeticaSession) stopSession(DefaultNeticaSession) ## Depricated methods StartNetica("License key from Norsys") StopNetica() ## Get the version of Netica. ## End(Not run)
## Not run: ## Recommended way of doing things DefaultNeticaSession <- NeticaSession(LicenseKey="Code from Norsys") startSession(DefaultNeticaSession) ## If DefaultNeticaSession was created in a previous R session ## Re-read the networks. for (netname in DefaultNeticaSession$listNets()) { net <- DefaultNeticaSession$findNet(netname) ReadNetworks(GetNetworkFileName(net),DefaultNeticaSession) } restartSession(DefaultNeticaSession) stopSession(DefaultNeticaSession) ## Depricated methods StartNetica("License key from Norsys") StopNetica() ## Get the version of Netica. ## End(Not run)
"NetworkTester"
The NetworkTester
object is created by calls to
the testNetwork
function, and contains the cached
results from the test. These function return the cached results.
testerNet(tester) testerTarget(tester) testerIgnore(tester) testerErrorRate(tester, node = NULL) testerLogLoss(tester, node = NULL) testerQuadraticLoss(tester, node = NULL) testerConfusion(tester, node = NULL) testerKappa(tester, node = NULL, weights = c("None", "Linear", "Quadratic"), W = NULL) testerLambda(tester, node = NULL, weights = c("None", "Linear", "Quadratic"), W = NULL) ## S3 method for class 'NetworkTester' summary(object, ...)
testerNet(tester) testerTarget(tester) testerIgnore(tester) testerErrorRate(tester, node = NULL) testerLogLoss(tester, node = NULL) testerQuadraticLoss(tester, node = NULL) testerConfusion(tester, node = NULL) testerKappa(tester, node = NULL, weights = c("None", "Linear", "Quadratic"), W = NULL) testerLambda(tester, node = NULL, weights = c("None", "Linear", "Quadratic"), W = NULL) ## S3 method for class 'NetworkTester' summary(object, ...)
tester |
An object of class |
node |
One of the target nodes for tester (an element of
|
weights |
Loss weights for Kappa and Lambda. See details. |
W |
A loss matrix for Kappa and Lambda. If |
object |
An object of class |
... |
Other arguments to |
The testNetwork
function takes as input generated data
with values for both the testTarget
nodes and the observed nodes
(the nodes that are not in the testerTarget
or
testerIgnore
lists. It estimates the values for the generated
nodes and compares the estimated values with the actual values from
the data set. The results are stashed in a
NetworkTester
object. The functions described
here extract information from the NetworkTester
object.
The function testerErrorRate
gives the fraction of cases where
the predicted value for the node was different from the actual value.
If node
is specified, the value for that node is returned,
otherwise a vector of values for all nodes are returned.
The log loss (also called the score) for target node
in case
is defined as the
;
it ranges from 0 to infinity with lower scores being better. The
function
testerLogLoss
gives the average log loss over all
available test cases. If node
is specified, the value for that
node is returned, otherwise a vector of values for all nodes are
returned.
The quadratic loss (also called the Brier score) for
target node in case
is defined as the
; it ranges from 0 to 2 with
lower scores being better. The function
testerQuadraticLoss
gives the average quadratic loss over all available test cases. If
node
is specified, the value for that node is returned,
otherwise a vector of values for all nodes are returned.
The function testerConfusion
returns the confusion matrixes
associated with each node in the testerTarget
node list. This
is a square matrix whose rows and columns correspond to the predicted
and actual values of the node respectively. The value in cell
of cases for which the predicted value for the node was in State
and the actual value was in State
. There is one
confusion matrix for each target node. If the
node
argument is
specified, then the confusion matrix for that node is returned,
otherwise a named list of confusion matrixes is returned.
Cohen's and Goodman and Kruskal's
provide
ways of summarizing a confusion matrix (Almond et al., 2015). One
obvious way to summarize the confusion matrix is to look at the
proportion of results along the main diagonal. This is the error rate
described above. However, if one category is very common in the
population, the error rate could be very high because of the base rate
and not because of the power of the classification system. Kappa and
lambda are two ways to normalize the error rate to avoid the base rate
issue. Kappa assumes that the two raters (the predicted and the
actual) are both rating by chance at their marginal frequencies. It
adjusts the base rate for the proportion of time the two agree by
chance. Lambda compares the predicted results to a predictor who
always predicts the most common category; thus is indicates how much
better a differentiated approach is to one that treats all cases
equally. Both values are numbers between -1 and 1 (similar to
correlations) with 1 indicating perfect agreement, -1 perfect
disagreement and 0 the two measures are unrelated.
For both kappa and lambda, the analyst can specify a weight matrix (1
- loss matrix) W
which indicates how desirable it is to
classify a subject as , when they really are a
. This
is usually scaled so that the main diagnoal is 1 and the smallest
value if 0 (or at least all values are positive). If the states are
ordered, common choices of weight are linear weights (weights decrease
linearly as they move from the diagonal) and quadratic weights
(weights decrease quadratically). These can be specified using the
keywords for the
weights
. The default keyword is “None”
which produces the identify matrix (the unweighted case).
The functions testerNet
returns the
NeticaBN
on which the test was run.
The functions testerTarget
and testerIgnore
both return
lists of NeticaNode
objects. In the first case
the target nodes.
The functions testerErrorRate
, testerLogLoss
,
testerQuadraticLoss
, testerKappa
and testerLambda
give a vector of values for the respecrive function. If node
is specified, the value for that node is returned, otherwise a named
vector of values for all nodes is returned.
The function testerConfusion
returns a single confusion matrix
if node
is specified or a named list of confusion matrixes.
The summary
method for NetworkTester
objects returns a data frame where the rows represent the target nodes
and the columns have the following values:
testerErrorRate(object)
testerLogLoss(object)
testerQuadraticLoss(object)
testerKappa(object)
testerKappa(object,weights="Quadratic")
testerLambda(object)
testerLambda(object,weights="Linear")
Russell Almond
Almond, R.G., Mislevy, R.J. Steinberg, L.S., Yan, D. and Willamson, D. M. (2015). Bayesian Networks in Educational Assessment. Springer. Chapter 7.
http://norsys.com/onLineAPIManual/index.html: NewNetTester_bn(), DeleteNetTester_bn(), TestWithCaseset_bn(), GetTestConfusion_bn(), GetTestErrorRate_bn(), GetTestLogLoss_bn(), GetTestQuadraticLoss_bn()
The output of testNetwork
is NetworkTester
.
sess <- NeticaSession() startSession(sess) irt5 <- ReadNetworks(system.file("sampleNets","IRT5.dne", package="RNetica"), session=sess) irt5.theta <- NetworkFindNode(irt5,"Theta") irt5.x <- NetworkFindNode(irt5,paste("Item",1:5,sep="_")) CompileNetwork(irt5) ## This generates a fixed series of random cases and saves them to a ## file. N <- 100L rnodes <- c(list(irt5.theta),irt5.x) casefile <- tempfile("irt5testcase",fileext=".cas") filestream <- CaseFileStream(casefile, session=sess) rng <- NewNeticaRNG(123456779, session=sess) WithOpenCaseStream(filestream, WithRNG(rng, for (n in 1L:N) { GenerateRandomCase(rnodes,rng=rng) WriteFindings(rnodes,filestream,n) lapply(rnodes,RetractNodeFinding) # Only retract findings for # generated nodes })) irt5.test <- testNetwork(list(irt5.theta),OpenCaseStream(filestream)) summary(irt5.test) DeleteNetwork(irt5) stopSession(sess)
sess <- NeticaSession() startSession(sess) irt5 <- ReadNetworks(system.file("sampleNets","IRT5.dne", package="RNetica"), session=sess) irt5.theta <- NetworkFindNode(irt5,"Theta") irt5.x <- NetworkFindNode(irt5,paste("Item",1:5,sep="_")) CompileNetwork(irt5) ## This generates a fixed series of random cases and saves them to a ## file. N <- 100L rnodes <- c(list(irt5.theta),irt5.x) casefile <- tempfile("irt5testcase",fileext=".cas") filestream <- CaseFileStream(casefile, session=sess) rng <- NewNeticaRNG(123456779, session=sess) WithOpenCaseStream(filestream, WithRNG(rng, for (n in 1L:N) { GenerateRandomCase(rnodes,rng=rng) WriteFindings(rnodes,filestream,n) lapply(rnodes,RetractNodeFinding) # Only retract findings for # generated nodes })) irt5.test <- testNetwork(list(irt5.theta),OpenCaseStream(filestream)) summary(irt5.test) DeleteNetwork(irt5) stopSession(sess)
This function takes a set of cases represented with a
CaseStream
and a network, and trys to classify the
values of the targetNodes
using the data in the cases. It
returns an object of class NetworkTester
which
contains statistics about the recovery of the target nodes from the
data.
testNetwork(targetNodes, dataStreams, ignoreNodes = list())
testNetwork(targetNodes, dataStreams, ignoreNodes = list())
targetNodes |
A list of active |
dataStreams |
A list of active (open)
|
ignoreNodes |
A (possibly empty) list of active
|
The tester behaves a follows: It opens a
NetworkTester
object to hold the results. It
then runs through the case streams. For each case, it instantiates
each node in the network to the findings as found in the case stream,
except for those nodes in targetNodes
or ignoreNodes
.
It then looks at the NodeBeliefs
associated with the
targetNodes
and compares these with the actual value of the
target nodes as found in the case stream. It accumulates statistics
related to the accuracy.
After all case streams are processed, the
NetworkTester
object is returned and its
statistics can be queried. See testerConfusion
for a
list of available results.
An object of class NetworkTester
.
Netica's internal net tester object can add additional case sets at a
later time. However, RNetica, closes this object at the completion of
the call to testNetwork
, so a new test needs to be performed to
add more data to the test.
Russell Almond
Almond, R.G., Mislevy, R.J. Steinberg, L.S., Yan, D. and Willamson, D. M. (2015). Bayesian Networks in Educational Assessment. Springer. Chapter 7.
http://norsys.com/onLineAPIManual/index.html: NewNetTester_bn(), DeleteNetTester_bn(), TestWithCaseset_bn(), GetTestConfusion_bn(), GetTestErrorRate_bn(), GetTestLogLoss_bn(), GetTestQuadraticLoss_bn()
The output of testNetwork
is NetworkTester
.
Accessors for NetworkTester
testerConfusion
, testerErrorRate
,
testerIgnore
, testerKappa
,
testerLambda
, testerLogLoss
,
testerNet
, testerQuadraticLoss
,
testerTarget
sess <- NeticaSession() startSession(sess) irt5 <- ReadNetworks(system.file("sampleNets","IRT5.dne", package="RNetica"), session=sess) irt5.theta <- NetworkFindNode(irt5,"Theta") irt5.x <- NetworkFindNode(irt5,paste("Item",1:5,sep="_")) CompileNetwork(irt5) ## This generates a fixed series of random cases and saves them to a ## file. N <- 100L rnodes <- c(list(irt5.theta),irt5.x) casefile <- tempfile("irt5testcase",fileext=".cas") filestream <- CaseFileStream(casefile, session=sess) rng <- NewNeticaRNG(123456779, session=sess) WithOpenCaseStream(filestream, WithRNG(rng, for (n in 1L:N) { GenerateRandomCase(rnodes,rng=rng) WriteFindings(rnodes,filestream,n) lapply(rnodes,RetractNodeFinding) # Only retract findings for # generated nodes })) irt5.test <- testNetwork(list(irt5.theta),OpenCaseStream(filestream)) summary(irt5.test) DeleteNetwork(irt5) stopSession(sess)
sess <- NeticaSession() startSession(sess) irt5 <- ReadNetworks(system.file("sampleNets","IRT5.dne", package="RNetica"), session=sess) irt5.theta <- NetworkFindNode(irt5,"Theta") irt5.x <- NetworkFindNode(irt5,paste("Item",1:5,sep="_")) CompileNetwork(irt5) ## This generates a fixed series of random cases and saves them to a ## file. N <- 100L rnodes <- c(list(irt5.theta),irt5.x) casefile <- tempfile("irt5testcase",fileext=".cas") filestream <- CaseFileStream(casefile, session=sess) rng <- NewNeticaRNG(123456779, session=sess) WithOpenCaseStream(filestream, WithRNG(rng, for (n in 1L:N) { GenerateRandomCase(rnodes,rng=rng) WriteFindings(rnodes,filestream,n) lapply(rnodes,RetractNodeFinding) # Only retract findings for # generated nodes })) irt5.test <- testNetwork(list(irt5.theta),OpenCaseStream(filestream)) summary(irt5.test) DeleteNetwork(irt5) stopSession(sess)
This function evaluates expr in a context where the
CaseStream
is open. The stream is closed when the
evaluation is complete. The evaluation of expr is surrounded
with a tryCatch
so that the stream is closed
whether or not the expression is successfully executed.
WithOpenCaseStream(stream, expr)
WithOpenCaseStream(stream, expr)
stream |
A |
expr |
An arbitrary R expression to be executed. |
Either the result of evaluating expr unless executing
expr results in an error in which case it
returns a try-error
.
Russell Almond
## This function reads findings from a stream until it finds one ## matching a certain case ID. ReadCase <- function (stream,nodes,caseID) { WithOpenCaseStream(stream, {stream <- ReadFindings(nodes,stream,"FIRST") while(!is.na(getCaseStreamPos(stream)) && getCaseStreamLastId(stream) != caseID) { ReadFindings(nodes,stream,"NEXT") } if (is.na(getCaseStreamPos(stream))) { warning("Case ID:",caseID," not found in stream.") } stream }) } sess <- NeticaSession() startSession(sess) ## Test it. abc <- CreateNetwork("ABC", session=sess) A <- NewDiscreteNode(abc,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc,"C",c("C1","C2")) AddLink(A,B) AddLink(A,C) AddLink(B,C) ## Input filename ## Note, this is a cached copy of the file written in the WriteFindings ## documentation. casefile <- system.file("testData","abctestcases.cas", package="RNetica") filestream <- ReadCase(CaseFileStream(casefile, session=sess),list(A,B,C),1002) stopifnot( !isCaseStreamOpen(filestream), NodeFinding(A) == "A2", NodeFinding(B) == "B2", NodeFinding(C) == "C2", getCaseStreamLastId(filestream)==1002, abs(getCaseStreamLastFreq(filestream)-2.0) < .0001) ##Clean Up DeleteNetwork(abc) stopSession(sess)
## This function reads findings from a stream until it finds one ## matching a certain case ID. ReadCase <- function (stream,nodes,caseID) { WithOpenCaseStream(stream, {stream <- ReadFindings(nodes,stream,"FIRST") while(!is.na(getCaseStreamPos(stream)) && getCaseStreamLastId(stream) != caseID) { ReadFindings(nodes,stream,"NEXT") } if (is.na(getCaseStreamPos(stream))) { warning("Case ID:",caseID," not found in stream.") } stream }) } sess <- NeticaSession() startSession(sess) ## Test it. abc <- CreateNetwork("ABC", session=sess) A <- NewDiscreteNode(abc,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc,"C",c("C1","C2")) AddLink(A,B) AddLink(A,C) AddLink(B,C) ## Input filename ## Note, this is a cached copy of the file written in the WriteFindings ## documentation. casefile <- system.file("testData","abctestcases.cas", package="RNetica") filestream <- ReadCase(CaseFileStream(casefile, session=sess),list(A,B,C),1002) stopifnot( !isCaseStreamOpen(filestream), NodeFinding(A) == "A2", NodeFinding(B) == "B2", NodeFinding(C) == "C2", getCaseStreamLastId(filestream)==1002, abs(getCaseStreamLastFreq(filestream)-2.0) < .0001) ##Clean Up DeleteNetwork(abc) stopSession(sess)
Calculates the weight of evidence provided by the current findings for
the specified hypothesis. A hypothesis consists of a statement that a
particular set of nodes (hnodes) will fall in a specified set
of states (hstatelists). The function ewoe
calculates
the expected weight of evidence for unobserved nodes.
woe(enodes, hnodes, hstatelists) ewoe(enodes, hnodes, hstatelists)
woe(enodes, hnodes, hstatelists) ewoe(enodes, hnodes, hstatelists)
enodes |
A list of |
hnodes |
A list of |
hstatelists |
A list of character vectors the same length as hnodes corresponding to the hypothesized state of the nodes and representing states of the corresponding node. As a special case, a character vector is turned into a list of length one. |
Good (1985) defines the weight of evidence for a hypothesis
as
For the function woe
, the evidence is taken as all findings
that do not include the hypothesis nodes (hnodes
; findings for
hnodes
are retracted).
A hypothesis is defined as a set of nodes and a set of possible values
that those nodes can take on. Thus hnodes
is a list of nodes,
and hstatelists
is a corresponding list of states, each element
of the list corresponding to a node. Note that each element of the
list can be a vector indicating the hypothesis that the corresponding
node is in one of the list of corresponding states. Thus
hnodes=list(Skill1))
and
hstatelists=list(c("High","Med"))
would indicate that
the hypothesis is that Skill1 is either High or Med. Only hypotheses
that are a Cartesian product of such variable assignments are
supported.
Note that if the hypothesis involves a single variable, there is a
simpler way to calculate weights of evidence which may be useful. In
this case, it is sufficient to create a history of the
NodeBeliefs
of the target variable as the evidence is
being entered. This can then be processed with
woeHist
or woeBal
.
The expected weight of evidence (ewoe
) looks at potential
future observations to find which might have the highest weight of
evidence. The expected weight of evidence is
Madigan and Almond (1995) note that the expected weight of evidence
can be calculated simultaneously for a number of different nodes. The
function ewoe
calculates the EWOE for all of the nodes in
targets
.
The MutualInfo
function will calculate the mutual
information between a single hypothesis node an several potential
evidence nodes. As this is a native Netica function, it may be
faster.
The function woe
returns the weight of evidence for the
specified hypothesis in centibans (100*log10).
Russell Almond
Good, I.J. (1985). Weight of Evidence: A brief survey. In Bernardo, J., DeGroot, M., Lindley, D. and Smith, A. (eds). Bayesian Statistics 2. North Holland. 249–269.
Madigan, D. and Almond, R. G. (1995). Test selection strategies for belief networks. In Fisher, D. and Lenz, H. J. (eds). Learning from Data: AI and Statistics V. Springer-Verlag. 89–98.
woeHist
, woeBal
,
NodeFinding
, NodeLikelihood
,
RetractNodeFinding
, MutualInfo
sess <- NeticaSession() startSession(sess) aced2 <- ReadNetworks(system.file("sampleNets","ACEDMotif2.dne", package="RNetica"), session=sess) aced2.obs <- NetworkNodesInSet(aced2,"Observables") aced2.prof <- NetworkNodesInSet(aced2,"Proficiencies") sgp <- aced2.prof$SolveGeometricProblems CompileNetwork(aced2) probHist <- matrix(NA,4,NodeNumStates(sgp), dimnames=list(paste("Evidence",1:4,sep=""), NodeStates(sgp))) probHist[1,] <- NodeBeliefs(sgp) rownames(probHist)[1] <- "*Baseline*" NodeFinding(aced2.obs$CommonRatioMediumTask) <- "True" probHist[2,] <- NodeBeliefs(sgp) rownames(probHist)[2] <- "CommonRatioMedium=True" woe1 <- woe(aced2.obs$CommonRatioMediumTask, list(sgp),list(c("High","Medium"))) NodeFinding(aced2.obs$RecursiveRuleMediumTask) <- "False" probHist[3,] <- NodeBeliefs(sgp) rownames(probHist)[3] <- "RecursiveRuleMedium=False" woe2 <- woe(aced2.obs$RecursiveRuleMediumTask, list(sgp),list(c("High","Medium"))) NodeFinding(aced2.obs$VisualMediumTask) <- "True" probHist[4,] <- NodeBeliefs(sgp) rownames(probHist)[4] <- "VisualMedium=True" woe3 <- woe(aced2.obs$VisualMediumTask, list(sgp),list(c("High","Medium"))) woehist <- woeHist(probHist,c("High","Medium"),"Low") stopifnot(all(abs((woehist-c(woe1,woe2,woe3)))<.0001)) unobsed <- sapply(aced2.obs,NodeFinding)=="@NO FINDING" woe(aced2.obs[!unobsed], list(aced2.prof$VerbalRuleGeometric, aced2.prof$ExplicitGeometric), list(c("High"),c("High","Medium"))) ewe <- ewoe(aced2.obs[unobsed],sgp,c("High","Medium")) ram <- MutualInfo(sgp,aced2.obs[unobsed]) stopifnot(all(order(ewe)==order(ram)))
sess <- NeticaSession() startSession(sess) aced2 <- ReadNetworks(system.file("sampleNets","ACEDMotif2.dne", package="RNetica"), session=sess) aced2.obs <- NetworkNodesInSet(aced2,"Observables") aced2.prof <- NetworkNodesInSet(aced2,"Proficiencies") sgp <- aced2.prof$SolveGeometricProblems CompileNetwork(aced2) probHist <- matrix(NA,4,NodeNumStates(sgp), dimnames=list(paste("Evidence",1:4,sep=""), NodeStates(sgp))) probHist[1,] <- NodeBeliefs(sgp) rownames(probHist)[1] <- "*Baseline*" NodeFinding(aced2.obs$CommonRatioMediumTask) <- "True" probHist[2,] <- NodeBeliefs(sgp) rownames(probHist)[2] <- "CommonRatioMedium=True" woe1 <- woe(aced2.obs$CommonRatioMediumTask, list(sgp),list(c("High","Medium"))) NodeFinding(aced2.obs$RecursiveRuleMediumTask) <- "False" probHist[3,] <- NodeBeliefs(sgp) rownames(probHist)[3] <- "RecursiveRuleMedium=False" woe2 <- woe(aced2.obs$RecursiveRuleMediumTask, list(sgp),list(c("High","Medium"))) NodeFinding(aced2.obs$VisualMediumTask) <- "True" probHist[4,] <- NodeBeliefs(sgp) rownames(probHist)[4] <- "VisualMedium=True" woe3 <- woe(aced2.obs$VisualMediumTask, list(sgp),list(c("High","Medium"))) woehist <- woeHist(probHist,c("High","Medium"),"Low") stopifnot(all(abs((woehist-c(woe1,woe2,woe3)))<.0001)) unobsed <- sapply(aced2.obs,NodeFinding)=="@NO FINDING" woe(aced2.obs[!unobsed], list(aced2.prof$VerbalRuleGeometric, aced2.prof$ExplicitGeometric), list(c("High"),c("High","Medium"))) ewe <- ewoe(aced2.obs[unobsed],sgp,c("High","Medium")) ram <- MutualInfo(sgp,aced2.obs[unobsed]) stopifnot(all(order(ewe)==order(ram)))
These functions our wrapper around read.table
and
write.table
to format the file in the expected
Netica case file format.
write.CaseFile(x, file, ..., session=getDefaultSession()) read.CaseFile(file, ..., session=getDefaultSession())
write.CaseFile(x, file, ..., session=getDefaultSession()) read.CaseFile(file, ..., session=getDefaultSession())
x |
A data frame to be written to the file. See details. |
file |
A file name or a connection object. By convention, Netica expects case files to end in the “.cas” suffix. |
... |
Other arguments to |
session |
An object of class |
A Netica case file has a format that very much resembles the output of
write.table
. The first row is a header row, which
contains the names of the variables, the second and subsequent rows
contain a set of findings: an assignment of values to the nodes
indicated in the columns. There are no row numbers, and the separator
and missing value codes are the values of
CaseFileDelimiter()
, and
CaseFileMissingCode()
respectively.
In addition to columns representing variables, two special columns are
allowed. The column named “IDnum”, if present should contain
integers which correspond to ID numbers for the cases (this correspond
to the id
argument of WriteFindings
). The column
named “NumCases” should contain number values and this allows
rows to be differentially weighted (this correspond to the freq
argument of WriteFindings
). If these special arguments
are present, write.table
permutes the columns if necessary to
make them first in the order (as Netica does in WriteFindings
).
The function read.CaseFile
overrides following arguments of
read.table
: header = TRUE
, sep =
CaseFileDelimiter()
, and na.strings =
CaseFileMissingCode()
. The function write.CaseFile
overrides following arguments of write.table
: col.name =
TRUE
, row.names = FALSE
, quote = FALSE
, sep =
CaseFileDelimiter()
, and na =
CaseFileMissingCode()
.
The function read.CaseFile
returns a data frame containing the
information in the case file. The function write.CaseFile
returns the output of the write.table
call (which
is undocumented).
Russell Almond
CaseFileDelimiter
, CaseFileMissingCode
,
WriteFindings
, ReadFindings
,
CaseMemoryStream
,CaseFileStream
,
MemoryStreamContents
,
read.table
,write.table
sess <- NeticaSession() startSession(sess) casefile <- system.file("testData", "abctestcases.cas", package="RNetica") CaseFileDelimiter("\t", session=sess) CaseFileMissingCode("*", session=sess) cases <- read.CaseFile(casefile, session=sess) outfile <- tempfile("testcase",fileext=".cas") write.CaseFile(cases,outfile, session=sess) stopSession(sess)
sess <- NeticaSession() startSession(sess) casefile <- system.file("testData", "abctestcases.cas", package="RNetica") CaseFileDelimiter("\t", session=sess) CaseFileMissingCode("*", session=sess) cases <- read.CaseFile(casefile, session=sess) outfile <- tempfile("testcase",fileext=".cas") write.CaseFile(cases,outfile, session=sess) stopSession(sess)
This function writes the current findings for a network as a row in a Netica case file. If filename already exists, the new row is appended on the end of the file. Variables that are not instantiated are written out using the missing code.
WriteFindings(nodes, pathOrStream, id = -1L, freq = -1.0)
WriteFindings(nodes, pathOrStream, id = -1L, freq = -1.0)
nodes |
The a list of active |
pathOrStream |
Either a character scalar giving the path name of
the file to which the results are to be written, or a
|
id |
An integer scalar giving the case ID. The default value of
|
freq |
An integer scalar giving the number of cases with the
currently instantiated set of findings. The default value |
A case file is a table where the rows represent cases, and the columns
represent variables. WriteFindings
writes out the
currently instantiated value of the nodes in nodeset. If a
node in nodeset does not currently have a finding attached,
then the value of CaseFileMissingCode()
is printed out
instead. The values in the columns are separated by the value of
CaseFileDelimiter()
.
There are two special columns in the file. The column “IDnum”
is set to the value id
, which should contain an integer case
number. The column “NumCases” is set to the value of
freq
which should give a weight to assign to the case (in
various algorithms when freq
is supplied, it is treated as if
that case was repeated weight
times). Assigning either of
these fields a value of -1
means the corresponding column is
appended to the output.
The function WriteFindings
will create a new file
associated with filename
if it does not exist. In that case it
will write out a header row containing the variable names followed by
the current findings as the first case row. Subsequent calls to
WriteFindings
with the same filename
append
additional rows to the end of the file. In such cases, the
nodelist
should be the same, and if id
or freq
was -1
, it should be in the following calls as well.
Returns the caseOrStream argument invisibly. Note that the
values of getCaseStreamPos(stream)
,
getCaseStreamLastId(stream)
, and
getCaseStreamLastFreq(stream)
will be updated to reflect
the values from the last read record.
Russell G. Almond
http://norsys.com/onLineAPIManual/index.html: WriteNetFindings_bn()
CaseFileDelimiter
, CaseFileMissingCode
,
NodeFinding
, RetractNetFindings
ReadFindings
, CaseStream
sess <- NeticaSession() startSession(sess) abc <- CreateNetwork("ABC", session=sess) A <- NewDiscreteNode(abc,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc,"C",c("C1","C2")) AddLink(A,B) AddLink(A,C) AddLink(B,C) ## Outputfilename casefile <- tempfile("testcase",fileext=".cas") filestream <- CaseFileStream(casefile, session=sess) stopifnot(is.CaseFileStream(filestream), isCaseStreamOpen(filestream)) ## Case 1 NodeFinding(A) <- "A1" NodeFinding(B) <- "B1" NodeFinding(C) <- "C1" WriteFindings(list(A,B,C),casefile,1) RetractNetFindings(abc) ## Case 2 NodeFinding(A) <- "A2" NodeFinding(B) <- "B2" NodeFinding(C) <- "C2" WriteFindings(list(A,B,C),casefile,2) RetractNetFindings(abc) ## Case 3 NodeFinding(A) <- "A3" NodeFinding(B) <- "B3" ## C will be missing WriteFindings(list(A,B,C),casefile,3) RetractNetFindings(abc) DeleteNetwork(abc) stopSession(sess)
sess <- NeticaSession() startSession(sess) abc <- CreateNetwork("ABC", session=sess) A <- NewDiscreteNode(abc,"A",c("A1","A2","A3","A4")) B <- NewDiscreteNode(abc,"B",c("B1","B2","B3")) C <- NewDiscreteNode(abc,"C",c("C1","C2")) AddLink(A,B) AddLink(A,C) AddLink(B,C) ## Outputfilename casefile <- tempfile("testcase",fileext=".cas") filestream <- CaseFileStream(casefile, session=sess) stopifnot(is.CaseFileStream(filestream), isCaseStreamOpen(filestream)) ## Case 1 NodeFinding(A) <- "A1" NodeFinding(B) <- "B1" NodeFinding(C) <- "C1" WriteFindings(list(A,B,C),casefile,1) RetractNetFindings(abc) ## Case 2 NodeFinding(A) <- "A2" NodeFinding(B) <- "B2" NodeFinding(C) <- "C2" WriteFindings(list(A,B,C),casefile,2) RetractNetFindings(abc) ## Case 3 NodeFinding(A) <- "A3" NodeFinding(B) <- "B3" ## C will be missing WriteFindings(list(A,B,C),casefile,3) RetractNetFindings(abc) DeleteNetwork(abc) stopSession(sess)
This function writes a Netica network to a .neta
or .dne
file or reads a network written by such a file. This allows networks
created with RNetica to be shared with other Netica users.
WriteNetworks(nets, paths) ReadNetworks(paths, session, loadVisual=TRUE) GetNetworkFileName(net, internal=FALSE)
WriteNetworks(nets, paths) ReadNetworks(paths, session, loadVisual=TRUE) GetNetworkFileName(net, internal=FALSE)
nets |
A single |
net |
A single |
paths |
A character vector of pathnames to |
session |
An object of type |
loadVisual |
A logical flag. If true, the visual information associated with the network will be loaded, although it is not used by RNetica, it will be saved for later. If false, the visual information will not be loaded, which will eliminate the potential for some problems. |
internal |
A logical scalar. If true, the actual Netica object will be consulted, if false, a cached value in the R object will be used. |
This method invokes the native Netica open and save functions to read
and write networks to .neta
or .dne
files. The
.neta
format is binary and more compact, while the .dne
format is ASCII and may be safer in some circumstances (such as when
used with a source control system). Netica figures out which format
to use based on the extension of the file, elements of paths
should end with .neta
or .dne
.
The function GetNetworkFileName()
returns the name of the last
file this network was saved to or read from. It cannot be set other
than through the WriteNetworks()
or ReadNetworks()
functions. Note that the filename is saved in both the R object and
the Netica object. If internal=TRUE
, then the Netica object
will be consulted. This will raise an error if the net is not
currently active.
To facilitate saving and restoring files across R sessions, both
ReadNetworks()
and WriteNetworks()
attach a
"Filename"
attribute to the object, which records the file just
read or written. GetNetworkFileName(net,internal=TRUE)
will not work after quitting and restarting Netica, but
net$PathnameName
should contain the same pathname. If
ReadNetworks()
is passed a NeticaBN
object (or a list of
such objects), it will attempt to read from the file referenced by the
"Filename"
attribute. Thus, calling net <-
WriteNetworks(net,path)
right before shutting down R and
net <- ReadNetworks(net,session)
right after
the call to startSession()
should restore the network.
Both ReadNetworks()
and WriteNetworks()
return a list of
NeticaBN
objects corresponding to the new
networks. In the case of a problem with one of the networks, the
corresponding entry will be set to NULL
. If the return list
has length 1, a single NeticaBN
object will be returned instead
of a list.
A "PathnameName"
field is added to the
NeticaBN
object
that is returned. This can be used to restore NeticaBN
objects
after an R session is restarted.
The loadVisual
flag controls whether or not the information on
the position of the nodes is loaded with the network. The default is
to load it, so it will be saved for opening the network in the GUI.
In version 5.04 of the Netica API, there appear to be some issues
related to visual information. In particular,
AbsorbNodes
can crash if some nodes have visual
information and others do not. I have also found situations where
there were two sets of visual information in the file, which was
preventing it from being read.
I'm hoping for a new version of the API soon, but until then, it might be more useful to load this with the flag false.
The demonstration version of Netica is limited to the size of the networks it will write (the limit is somewhere around 10 nodes). If you are running across errors saving large networks, you need to purchase a Netica API license from Norsys (http://norsys.com/).
ReadNetworks()
and WriteNetworks()
are vectorized, and
can take either scalars or vectors as arguments (thus, a whole
collection of networks can be read or written at once). When the
argument is a scalar, a scalar is returned. This is probably the 80% case,
but may produce unexpected behavior in certain coding circumstances.
Netica does not seem to expand the character '~' to your home directory (unlike R which follows the Unix convention in this regard).
Russell Almond
http://norsys.com/onLineAPIManual/index.html: WriteNet_bn(), ReadNet_bn()
NeticaBN
, CreateNetwork()
,
NetworkFindNode()
(for recreating links to nodes after
restoring a net)
sess <- NeticaSession() startSession(sess) peanut <- CreateNetwork("peanut", session=sess) NetworkTitle(peanut) <- "The Peanut Network" peanutFile <- tempfile("peanut",fileext=".dne") WriteNetworks(peanut,peanutFile) stopifnot(GetNetworkFileName(peanut)==peanutFile) pecan <- CreateNetwork("pecan", session=sess) NetworkTitle(pecan) <- "The Pecan Network" pecanFile <- tempfile("pecan",fileext=".dne") almond <- CreateNetwork("almond", session=sess) NetworkTitle(almond) <- "The Almond Network" almondFile <- tempfile("almond",fileext=".neta") WriteNetworks(list(pecan,almond),c(pecanFile,almondFile)) stopifnot(GetNetworkFileName(pecan)==pecanFile, GetNetworkFileName(almond)==almondFile) DeleteNetwork(peanut) DeleteNetwork(pecan) DeleteNetwork(almond) stopifnot(!is.active(almond)) peanut <- ReadNetworks(peanutFile, session=sess) stopifnot(is.active(peanut)) stopifnot(NetworkTitle(peanut)=="The Peanut Network") stopifnot(GetNetworkFileName(peanut)==peanutFile) nets <- ReadNetworks(c(pecanFile,almondFile), session=sess) stopifnot(length(nets)==2) stopifnot(all(sapply(nets,is.active))) stopifnot(NetworkTitle(nets[[1]])=="The Pecan Network") almond <- GetNamedNetworks("almond", session=sess) stopifnot(is.NeticaBN(almond),is.active(almond)) stopifnot(GetNetworkFileName(pecan)==pecanFile, GetNetworkFileName(almond)==almondFile) DeleteNetwork(peanut) DeleteNetwork(nets[[1]]) DeleteNetwork(almond) stopSession(sess) ## Not run: ## Safe way to preserve node and network objects across R sessions. tnet <- WriteNetworks(tnet,"Tnet.neta") q(save="yes") # R library(RNetica) sess <- startSession(getDefaultSession()) tnet <- ReadNetworks(tnet, session=sess) nodes <- NetworkFindNodes(tnet,tnet$listNodes()) ## End(Not run)
sess <- NeticaSession() startSession(sess) peanut <- CreateNetwork("peanut", session=sess) NetworkTitle(peanut) <- "The Peanut Network" peanutFile <- tempfile("peanut",fileext=".dne") WriteNetworks(peanut,peanutFile) stopifnot(GetNetworkFileName(peanut)==peanutFile) pecan <- CreateNetwork("pecan", session=sess) NetworkTitle(pecan) <- "The Pecan Network" pecanFile <- tempfile("pecan",fileext=".dne") almond <- CreateNetwork("almond", session=sess) NetworkTitle(almond) <- "The Almond Network" almondFile <- tempfile("almond",fileext=".neta") WriteNetworks(list(pecan,almond),c(pecanFile,almondFile)) stopifnot(GetNetworkFileName(pecan)==pecanFile, GetNetworkFileName(almond)==almondFile) DeleteNetwork(peanut) DeleteNetwork(pecan) DeleteNetwork(almond) stopifnot(!is.active(almond)) peanut <- ReadNetworks(peanutFile, session=sess) stopifnot(is.active(peanut)) stopifnot(NetworkTitle(peanut)=="The Peanut Network") stopifnot(GetNetworkFileName(peanut)==peanutFile) nets <- ReadNetworks(c(pecanFile,almondFile), session=sess) stopifnot(length(nets)==2) stopifnot(all(sapply(nets,is.active))) stopifnot(NetworkTitle(nets[[1]])=="The Pecan Network") almond <- GetNamedNetworks("almond", session=sess) stopifnot(is.NeticaBN(almond),is.active(almond)) stopifnot(GetNetworkFileName(pecan)==pecanFile, GetNetworkFileName(almond)==almondFile) DeleteNetwork(peanut) DeleteNetwork(nets[[1]]) DeleteNetwork(almond) stopSession(sess) ## Not run: ## Safe way to preserve node and network objects across R sessions. tnet <- WriteNetworks(tnet,"Tnet.neta") q(save="yes") # R library(RNetica) sess <- startSession(getDefaultSession()) tnet <- ReadNetworks(tnet, session=sess) nodes <- NetworkFindNodes(tnet,tnet$listNodes()) ## End(Not run)