In this vignette, we present the main functionalities and data
structures of the fcaR
package when working with
implications in FCA.
We load the fcaR
package by:
library(fcaR)
We are going to work with two datasets, a crisp one and a fuzzy one.
The classical (binary) dataset is the well-known planets
formal context, presented in
Wille R (1982). “Restructuring Lattice Theory: An Approach Based on Hierarchies of Concepts.” In Ordered Sets, pp. 445–470. Springer.
::kable(planets, format = "html", booktabs = TRUE) knitr
small | medium | large | near | far | moon | no_moon | |
---|---|---|---|---|---|---|---|
Mercury | 1 | 0 | 0 | 1 | 0 | 0 | 1 |
Venus | 1 | 0 | 0 | 1 | 0 | 0 | 1 |
Earth | 1 | 0 | 0 | 1 | 0 | 1 | 0 |
Mars | 1 | 0 | 0 | 1 | 0 | 1 | 0 |
Jupiter | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
Saturn | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
Uranus | 0 | 1 | 0 | 0 | 1 | 1 | 0 |
Neptune | 0 | 1 | 0 | 0 | 1 | 1 | 0 |
Pluto | 1 | 0 | 0 | 0 | 1 | 1 | 0 |
The other formal context is fuzzy and is defined by the following matrix I:
::kable(I, format = "html", booktabs = TRUE) knitr
P1 | P2 | P3 | P4 | P5 | P6 | |
---|---|---|---|---|---|---|
O1 | 0.0 | 0.0 | 0.5 | 0.5 | 1.0 | 0 |
O2 | 1.0 | 1.0 | 1.0 | 0.0 | 0.0 | 0 |
O3 | 0.5 | 0.5 | 0.0 | 0.0 | 0.0 | 1 |
O4 | 0.0 | 0.0 | 0.0 | 1.0 | 0.5 | 0 |
O5 | 0.0 | 0.0 | 1.0 | 0.5 | 0.0 | 0 |
O6 | 0.5 | 0.5 | 0.0 | 0.0 | 0.0 | 1 |
Although ImplicationSet
objects can be created ad
hoc, the usual way to get implications is by the application of the
NextClosure algorithm to a FormalContext
object.
Thus, let us create different formal contexts with the previous datasets:
<- FormalContext$new(planets)
fc_planets <- FormalContext$new(I) fc_I
The function find_implications()
use the NextClosure
algorithm in a formal context to extract the canonical basis of
implications:
$find_implications()
fc_planets$find_implications() fc_I
We can inspect the implications by doing:
$implications
fc_planets#> Implication set with 10 implications.
#> Rule 1: {no_moon} -> {small, near}
#> Rule 2: {far} -> {moon}
#> Rule 3: {near} -> {small}
#> Rule 4: {large} -> {far, moon}
#> Rule 5: {medium} -> {far, moon}
#> Rule 6: {medium, large, far, moon} -> {small, near, no_moon}
#> Rule 7: {small, near, moon, no_moon} -> {medium, large, far}
#> Rule 8: {small, near, far, moon} -> {medium, large, no_moon}
#> Rule 9: {small, large, far, moon} -> {medium, near, no_moon}
#> Rule 10: {small, medium, far, moon} -> {large, near, no_moon}
$implications
fc_I#> Implication set with 12 implications.
#> Rule 1: {P6 [0.5]} -> {P1 [0.5], P2 [0.5], P6}
#> Rule 2: {P5 [0.5]} -> {P4 [0.5]}
#> Rule 3: {P3 [0.5], P4 [0.5], P5 [0.5]} -> {P5}
#> Rule 4: {P3 [0.5], P4} -> {P3}
#> Rule 5: {P3, P4 [0.5], P5} -> {P4}
#> Rule 6: {P2 [0.5]} -> {P1 [0.5]}
#> Rule 7: {P1 [0.5]} -> {P2 [0.5]}
#> Rule 8: {P1 [0.5], P2 [0.5], P4 [0.5]} -> {P1, P2, P3, P4, P5, P6}
#> Rule 9: {P1 [0.5], P2 [0.5], P3 [0.5]} -> {P1, P2, P3}
#> Rule 10: {P1 [0.5], P2} -> {P1}
#> Rule 11: {P1, P2 [0.5]} -> {P2}
#> Rule 12: {P1, P2, P3, P6} -> {P4, P5}
Internally, an ImplicationSet
consists of two matrices
(one for the left-hand sides and the other for the right-hand sides of
the rules). We can get these (sparse) matrices as:
$implications$get_LHS_matrix()
fc_planets#> 7 x 10 sparse Matrix of class "dgCMatrix"
#> [[ suppressing 10 column names '1', '2', '3' ... ]]
#>
#> small . . . . . . 1 1 1 1
#> medium . . . . 1 1 . . . 1
#> large . . . 1 . 1 . . 1 .
#> near . . 1 . . . 1 1 . .
#> far . 1 . . . 1 . 1 1 1
#> moon . . . . . 1 1 1 1 1
#> no_moon 1 . . . . . 1 . . .
$implications$get_RHS_matrix()
fc_planets#> 7 x 10 sparse Matrix of class "dgCMatrix"
#> [[ suppressing 10 column names '1', '2', '3' ... ]]
#>
#> small 1 . 1 . . 1 . . . .
#> medium . . . . . . 1 1 1 .
#> large . . . . . . 1 1 . 1
#> near 1 . . . . 1 . . 1 1
#> far . . . 1 1 . 1 . . .
#> moon . 1 . 1 1 . . . . .
#> no_moon . . . . . 1 . 1 1 1
The main practical use of an ImplicationSet
is to
compute the closure of a set of attributes, by using the
closure()
function:
# Let us build a set of attributes
<- Set$new(attributes = fc_planets$attributes)
S $assign(large = 1, far = 1)
S
S#> {large, far}
$implications$closure(S)$closure
fc_planets#> {large, far, moon}
We can check if an ImplicationSet
holds in a
FormalContext
by using the %holds_in%
operator:
# Let us clone the implication basis
<- fc_planets$implications$clone()
imps %holds_in% fc_planets
imps #> [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
Each component of this vector represents whether the corresponding
implication holds in the formal context. In this case, as the
ImplicationSet
is the Duquenne-Guigues basis for the
FormalContext
, all implications hold.
Conversely, we can check if a list of attribute sets (or a formal
context) respects an ImplicationSet
, via the
%respects%
operator:
%respects% imps
fc_planets #> 9 x 10 sparse Matrix of class "lgCMatrix"
#> [[ suppressing 10 column names 'imp_01', 'imp_02', 'imp_03' ... ]]
#>
#> set_1 | | | | | | | | | |
#> set_2 | | | | | | | | | |
#> set_3 | | | | | | | | | |
#> set_4 | | | | | | | | | |
#> set_5 | | | | | | | | | |
#> set_6 | | | | | | | | | |
#> set_7 | | | | | | | | | |
#> set_8 | | | | | | | | | |
#> set_9 | | | | | | | | | |
The result is a matrix where each row correspond to a attribute set
and each column to an implication. An element is TRUE
if
the corresponding set respects the corresponding implication. If the
first argument is a FormalContext
, the function will
consider the set of attributes of each object.
Some quantities can be computed for an
ImplicationSet
:
$implications$cardinality()
fc_planets#> [1] 10
<- fc_planets$implications$size()
sizes # Total number of attributes in the LHS and the RHS
colSums(sizes)
#> LHS RHS
#> 25 23
$implications$support()
fc_planets#> [1] 0.2222222 0.5555556 0.4444444 0.2222222 0.2222222 0.0000000 0.0000000
#> [8] 0.0000000 0.0000000 0.0000000
A nice feature is the ability to export an
ImplicationSet
to LaTeX format:
$implications$to_latex()
fc_planets#> \begin{longtable*}{rrcl}
#> 1: &\left\{\mathrm{no\_moon}\right\}&\ensuremath{\Rightarrow}&\left\{\mathrm{small}, \mathrm{near}\right\}\\
#> 2: &\left\{\mathrm{far}\right\}&\ensuremath{\Rightarrow}&\left\{\mathrm{moon}\right\}\\
#> 3: &\left\{\mathrm{near}\right\}&\ensuremath{\Rightarrow}&\left\{\mathrm{small}\right\}\\
#> 4: &\left\{\mathrm{large}\right\}&\ensuremath{\Rightarrow}&\left\{\mathrm{far}, \mathrm{moon}\right\}\\
#> 5: &\left\{\mathrm{medium}\right\}&\ensuremath{\Rightarrow}&\left\{\mathrm{far}, \mathrm{moon}\right\}\\
#> 6: &\left\{\mathrm{medium}, \mathrm{large}, \mathrm{far}, \mathrm{moon}\right\}&\ensuremath{\Rightarrow}&\left\{\mathrm{small}, \mathrm{near}, \mathrm{no\_moon}\right\}\\
#> 7: &\left\{\mathrm{small}, \mathrm{near}, \mathrm{moon}, \mathrm{no\_moon}\right\}&\ensuremath{\Rightarrow}&\left\{\mathrm{medium}, \mathrm{large}, \mathrm{far}\right\}\\
#> 8: &\left\{\mathrm{small}, \mathrm{near}, \mathrm{far}, \mathrm{moon}\right\}&\ensuremath{\Rightarrow}&\left\{\mathrm{medium}, \mathrm{large}, \mathrm{no\_moon}\right\}\\
#> 9: &\left\{\mathrm{small}, \mathrm{large}, \mathrm{far}, \mathrm{moon}\right\}&\ensuremath{\Rightarrow}&\left\{\mathrm{medium}, \mathrm{near}, \mathrm{no\_moon}\right\}\\
#> 10: &\left\{\mathrm{small}, \mathrm{medium}, \mathrm{far}, \mathrm{moon}\right\}&\ensuremath{\Rightarrow}&\left\{\mathrm{large}, \mathrm{near}, \mathrm{no\_moon}\right\}\\
#> \end{longtable*}
Sometimes it is needed to work with a subset of the implications, using only the implications that fulfill certain conditions:
# Implications with P1 and P2 in the LHS and P5 in the RHS
$implications$filter(lhs = c("P1", "P2"),
fc_Irhs = "P5")
#> Implication set with 2 implications.
#> Rule 1: {P1 [0.5], P2 [0.5], P4 [0.5]} -> {P1, P2, P3, P4, P5, P6}
#> Rule 2: {P1, P2, P3, P6} -> {P4, P5}
In this package, we have implemented logic tools to operate on the implications.
First, some simplification rules have been developed, named
reduction, composition, generalization and
simplification, that can be applied using the
apply_rules()
function:
$implications$apply_rules(rules = c("composition",
fc_I"simplification"))
#> Processing batch
#> --> Composition: from 12 to 12 in 0.001 secs.
#> --> Simplification: from 12 to 12 in 0.028 secs.
#> Batch took 0.029 secs.
This enables the reduction of the cardinality and/or the size of the
ImplicationSet
.
In addition, the “simplification” rule to remove redundancies can be used in the computation of the closure of a set, to provide a reduced set of implications that is inferred from the set of attributes:
# Let us build a set of attributes
<- Set$new(attributes = fc_planets$attributes)
S $assign(large = 1, far = 1)
S
S#> {large, far}
$implications$closure(S, reduce = TRUE)
fc_planets#> $closure
#> {large, far, moon}
#>
#> $implications
#> Implication set with 4 implications.
#> Rule 1: {medium} -> {small, near, no_moon}
#> Rule 2: {small} -> {medium, near, no_moon}
#> Rule 3: {no_moon} -> {small, near}
#> Rule 4: {near} -> {small}
We can check if a given ImplicationSet
follows from
another one:
# imps is the basis
<- fc_planets$implications$clone()
imps <- imps$clone()
imps2 # imps2 is an equivalent set of implications
# where we have removed redundancies
$apply_rules(c("simp", "rsimp"))
imps2#> Processing batch
#> --> Simplification: from 10 to 10 in 0.005 secs.
#> --> Right Simplification: from 10 to 10 in 0.023 secs.
#> Batch took 0.029 secs.
# Any implication in imps2 follows from imps
%entails% imps2
imps #> [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
#> [1,] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
# And viceversa
%entails% imps
imps2 #> [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
#> [1,] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
We can also check if the two sets of implications are equivalent:
%~% imps2
imps #> [1] TRUE
# If we remove any implication from imps2,
# they will not be equivalent
%~% imps2[1:9]
imps #> [1] FALSE
One of the final applications of an ImplicationSet
is
the easy development of a recommendation system where, from an attribute
set, the system would infer the value to other attribute. This is done
by the recommend()
function, which internally computes the
closure of the attribute set:
<- Set$new(attributes = fc_I$attributes)
S $assign(P1 = 1, P4 = 0.5)
S
$implications$recommend(S, attribute_filter = c("P3", "P5"))
fc_I#> P3 P5
#> 1 1