🐍 Iris sample#
This notebook walks through building a formal concept lattice from the Iris dataset using GALACTIC.
Creating item universe#
Load the Iris CSV dataset and wrap its rows into an item universe used by the later algebraic steps.
[3]:
from galactic.algebras.concept.core import ItemUniverse
[4]:
item_universe = ItemUniverse(dataset)
len(item_universe)
[4]:
150
Creating characteristics#
Define numerical and categorical characteristics that represent the Iris measurements and species label.
[5]:
from galactic.algebras.convex.characteristics.core import Component, Number, String
[6]:
sepal_length = Number(
components=(Component(expr='"sepal length"'),),
name="sepal_length",
cache=150,
)
sepal_width = Number(
components=(Component(expr='"sepal width"'),),
name="sepal_width",
cache=150,
)
petal_length = Number(
components=(Component(expr='"petal length"'),),
name="petal_length",
cache=150,
)
petal_width = Number(
components=(Component(expr='"petal width"'),),
name="petal_width",
cache=150,
)
klass = String(
components=(Component(expr="class"),),
name="class",
)
Creating predicate universe#
Build the predicate universe by pairing each characteristic with the corresponding numerical or categorical description.
[7]:
from galactic.algebras.convex.descriptions.numerical.hull.core import (
NumericalDescription,
)
from galactic.algebras.convex.descriptions.category.basic.core import (
CategoryDescription,
)
from galactic.algebras.convex.descriptions.core import (
PredicateUniverse,
)
[8]:
sepal_length_description = NumericalDescription(space=(sepal_length,))
sepal_width_description = NumericalDescription(space=(sepal_width,))
petal_length_description = NumericalDescription(space=(petal_length,))
petal_width_description = NumericalDescription(space=(petal_width,))
klass_description = CategoryDescription(space=(klass,))
[9]:
predicate_universe = PredicateUniverse(
descriptions=(
sepal_length_description,
sepal_width_description,
petal_length_description,
petal_width_description,
klass_description,
)
)
len(predicate_universe)
[9]:
0
Creating context and Galois connection#
Create the formal context from items and predicates, then derive the associated Galois connection.
[10]:
from galactic.algebras.convex.descriptions.core import (
Context,
GaloisConnection,
)
[11]:
context = Context(item_universe, predicate_universe)
[12]:
connection = GaloisConnection(context)
Creating top concept#
Instantiate the top concept from the connection and inspect its intent and domain sizes.
[13]:
from galactic.algebras.convex.descriptions.core import (
Concept,
)
[14]:
top = Concept(connection)
[str(attr) for attr in top.intent]
[14]:
['sepal_length ≥ 4.3',
'sepal_length ≤ 7.9',
'sepal_width ≥ 2.0',
'sepal_width ≤ 4.4',
'petal_length ≥ 1.0',
'petal_length ≤ 6.9',
'petal_width ≥ 0.1',
'petal_width ≤ 2.5',
"class ∈ {'Iris-setosa', 'Iris-versicolor', 'Iris-virginica'}"]
[15]:
(150, 9)
Creating lattice#
Initialise an extensible concept lattice from the top concept and render it as a Hasse diagram.
[16]:
from galactic.algebras.concept.core import ExtensibleLattice
[17]:
lattice = ExtensibleLattice([top])
[18]:
from galactic.algebras.concept.renderer import (
ConceptRenderer,
ConceptHasseDiagramDrawer,
)
[19]:
drawer = ConceptHasseDiagramDrawer(domain_renderer=ConceptRenderer(display_items=False))
drawer.draw(lattice)
[19]:
Creating quantile strategies#
Prepare quantile-based split strategies for each numerical feature to generate candidate concept refinements.
[20]:
from galactic.algebras.convex.strategies.numerical.quantile.core import (
QuantileStrategy,
)
[21]:
strategy_sepal_length = QuantileStrategy(
space=(sepal_length,),
cutoff=None,
quantile=None,
)
strategy_sepal_width = QuantileStrategy(
space=(sepal_width,),
cutoff=None,
quantile=None
)
strategy_petal_length = QuantileStrategy(
space=(petal_length,),
cutoff=None,
quantile=None
)
strategy_petal_width = QuantileStrategy(
space=(petal_width,),
cutoff=None,
quantile=None
)
Creating composite strategy#
Combine the feature-level quantile strategies into a single composite strategy.
[22]:
from galactic.algebras.convex.strategies.core import CompositeStrategy
[23]:
mixed_strategy=CompositeStrategy(
strategies=(
strategy_sepal_length,
strategy_sepal_width,
strategy_petal_length,
strategy_petal_width,
)
)
Creating selective strategy#
Wrap the composite strategy with entropy-based selection to keep the most informative refinements.
[24]:
from galactic.algebras.convex.strategies.core import SelectiveStrategy
from galactic.algebras.convex.measures.entropy.core import Entropy
[25]:
entropy = Entropy(category=klass)
selective_strategy = SelectiveStrategy(
strategies=(mixed_strategy,),
measure=entropy,
ratio=1,
)
[26]:
concepts = list(selective_strategy(top))
[27]:
lattice.extend(concepts)
[28]:
(2, 2, 373)
[29]:
drawer.draw(lattice)
[29]:
Applying the recursive strategy#
Apply recursion over the selected refinements to iteratively expand the concept lattice, limiting the support of recursion to 45.
[30]:
from galactic.algebras.convex.strategies.core import RecursiveStrategy
[31]:
recursive_strategy = RecursiveStrategy(
strategies=(selective_strategy,),
support=45,
)
[32]:
concepts = list(recursive_strategy(top))
[33]:
lattice.extend(concepts)
[34]:
(7, 6, 373)
[35]:
drawer.draw(lattice)
[35]: