/* * Copyright (c) 2017 OBiBa. All rights reserved. * * This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.obiba.magma.math; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Set; import javax.validation.constraints.NotNull; import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics; import org.obiba.magma.AbstractVariableValueSource; import org.obiba.magma.Datasource; import org.obiba.magma.Initialisable; import org.obiba.magma.NoSuchValueSetException; import org.obiba.magma.Timestamps; import org.obiba.magma.Value; import org.obiba.magma.ValueSet; import org.obiba.magma.ValueTable; import org.obiba.magma.ValueType; import org.obiba.magma.Variable; import org.obiba.magma.VariableEntity; import org.obiba.magma.VariableValueSource; import org.obiba.magma.VectorSource; import org.obiba.magma.VectorSourceNotSupportedException; import org.obiba.magma.support.AbstractValueTable; import org.obiba.magma.support.NullTimestamps; import org.obiba.magma.support.ValueSetBean; import org.obiba.magma.support.VariableEntityBean; import org.obiba.magma.support.VariableEntityProvider; import org.obiba.magma.type.DecimalType; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; /** * A {@code ValueTable} implementation that will compute a statistical summary for all numerical variables of another * table. Entities of this table are the {@code Variables} of the other. The variables of this table are the available * univariate statistics (mean, min, max, sum, etc.). */ @SuppressWarnings("UnusedDeclaration") public class SummaryStatisticsView extends AbstractValueTable implements Initialisable { private final ValueTable valueTable; private final DescriptiveStatisticsProvider statsProvider = new DefaultDescriptiveStatisticsProvider(); public SummaryStatisticsView(Datasource ds, String name, ValueTable valueTable) { super(ds, name); if(valueTable == null) throw new IllegalArgumentException("valueTable cannot be null"); this.valueTable = valueTable; } @Override public void initialise() { // Each variable in the wrapped table becomes a valueSet in this table setVariableEntityProvider(new AggregateVariableEntityProvider()); addVariableValueSources(ImmutableSet.<VariableValueSource>of(// new StatVariableValueSource("Min"), new StatVariableValueSource("Max"), new StatVariableValueSource("Mean"),// new StatVariableValueSource("GeometricMean"), new StatVariableValueSource("n"), new StatVariableValueSource("Sum"),// new StatVariableValueSource("SumSq"), new StatVariableValueSource("StandardDeviation"), new StatVariableValueSource("Variance"),// new StatVariableValueSource("Skewness"), new StatVariableValueSource("Kurtosis"))); } @NotNull @Override public Timestamps getTimestamps() { return NullTimestamps.get(); } @Override public ValueSet getValueSet(VariableEntity entity) throws NoSuchValueSetException { return new AggregateValueSet(entity); } @Override public Timestamps getValueSetTimestamps(VariableEntity entity) throws NoSuchValueSetException { return NullTimestamps.get(); } private class AggregateValueSet extends ValueSetBean { private final DescriptiveStatistics ds; protected AggregateValueSet(VariableEntity entity) { super(SummaryStatisticsView.this, entity); ds = statsProvider.compute(valueTable.getVariableValueSource(entity.getIdentifier()), Sets.newTreeSet(valueTable.getVariableEntities())); } DescriptiveStatistics getStats() { return ds; } } private class StatVariableValueSource extends AbstractVariableValueSource implements VariableValueSource { private final String statName; private final Method getter; private StatVariableValueSource(String name) { statName = name; getter = Iterables.find(Arrays.asList(DescriptiveStatistics.class.getMethods()), new Predicate<Method>() { @Override public boolean apply(Method input) { return input.getName().equalsIgnoreCase("get" + statName); } }); } @NotNull @Override public Variable getVariable() { return Variable.Builder.newVariable(statName, DecimalType.get(), getEntityType()).build(); } @NotNull @Override public Value getValue(ValueSet valueSet) { try { return DecimalType.get().valueOf(getter.invoke(((AggregateValueSet) valueSet).getStats())); } catch(Exception e) { throw new RuntimeException(e); } } @NotNull @Override public ValueType getValueType() { return DecimalType.get(); } @Override public boolean supportVectorSource() { return false; } @NotNull @Override public VectorSource asVectorSource() { throw new VectorSourceNotSupportedException(getClass()); } } private class AggregateVariableEntityProvider implements VariableEntityProvider { @NotNull @Override public String getEntityType() { return "Variable"; } @NotNull @Override public Set<VariableEntity> getVariableEntities() { return ImmutableSet.copyOf(Iterables .transform(Iterables.filter(valueTable.getVariables(), new UnivariateFilter()), new VariableToEntity())); } @Override public boolean isForEntityType(String entityType) { return getEntityType().equals(entityType); } } /** * Transforms a Variable to a VariableEntity */ private class VariableToEntity implements Function<Variable, VariableEntity> { @Override public VariableEntity apply(Variable from) { return new VariableEntityBean(getEntityType(), from.getName()); } } /** * Returns true when a variable's type is numeric */ private static class UnivariateFilter implements Predicate<Variable> { @Override public boolean apply(Variable input) { return input.getValueType().isNumeric(); } } }