
smile.data.formula.FactorInteraction Maven / Gradle / Ivy
/*
* Copyright (c) 2010-2021 Haifeng Li. All rights reserved.
*
* Smile is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Smile is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Smile. If not, see .
*/
package smile.data.formula;
import java.util.*;
import java.util.stream.Collectors;
import smile.data.Tuple;
import smile.data.measure.CategoricalMeasure;
import smile.data.measure.NominalScale;
import smile.data.type.DataTypes;
import smile.data.type.StructField;
import smile.data.type.StructType;
/**
* The interaction of all the factors appearing in the term.
* The interaction of one factor with another is that each
* of its levels is tested in each level of the other factor.
* For example, in a test of influences on crop yield, a watering
* regime factor (W: wet and dry) is crossed with a sowing density
* factor (D: high and low) when the response to the wet regime
* is tested at both high and low sowing density, and so is the
* response to the dry regime. If each of the four combinations
* of levels has replicate observations, then a cross-factored
* analysis can test for an interaction between the two treatment
* factors in their effect on the response.
*
* @author Haifeng Li
*/
public class FactorInteraction implements Term {
/** The factors of interaction. */
private final List factors;
/**
* Constructor.
*
* @param factors the factors of interaction.
*/
public FactorInteraction(String... factors) {
if (factors.length < 2) {
throw new IllegalArgumentException("Interaction() takes at least two factors");
}
this.factors = Arrays.asList(factors);
}
/**
* Returns the number of factors in the interaction.
* @return the number of factors in the interaction.
*/
public int size() {
return factors.size();
}
@Override
public String toString() {
return String.join(":", factors);
}
@Override
public Set variables() {
return new HashSet<>(factors);
}
@Override
public List bind(StructType schema) {
List fields = factors.stream()
.map(schema::field)
.toList();
for (StructField field : fields) {
if (!(field.measure instanceof CategoricalMeasure)) {
throw new IllegalStateException(String.format("%s is not a categorical variable: %s", field.name, field.measure));
}
}
List levels = new ArrayList<>();
levels.add("");
for (StructField field : fields) {
CategoricalMeasure cat = (CategoricalMeasure) field.measure;
levels = levels.stream()
.flatMap(l -> Arrays.stream(cat.levels()).map(level -> l.isEmpty() ? level : l + ":" + level))
.collect(Collectors.toList());
}
return Collections.singletonList(new InteractionFeature(levels));
}
/**
* An interaction feature.
*/
private class InteractionFeature implements Feature {
final NominalScale measure ;
final StructField field;
InteractionFeature(List levels) {
measure = new NominalScale(levels);
field = new StructField(
String.join(":", factors),
DataTypes.IntegerType,
measure
);
}
@Override
public String toString() {
return field.name;
}
@Override
public StructField field() {
return field;
}
@Override
public int applyAsInt(Tuple o) {
String level = factors.stream().map(o::getString).collect(Collectors.joining(":"));
return measure.valueOf(level).intValue();
}
@Override
public Object apply(Tuple o) {
String level = factors.stream().map(o::getString).collect(Collectors.joining(":"));
return measure.valueOf(level);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy