/* * 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.util.Map; import javax.annotation.Nullable; import javax.validation.constraints.NotNull; import org.obiba.magma.Value; import org.obiba.magma.ValueSet; import org.obiba.magma.VariableValueSource; import org.obiba.magma.VariableValueSourceWrapper; import org.obiba.magma.transform.BijectiveFunction; import org.obiba.magma.views.View; import com.google.common.collect.Maps; /** * A {@code View} that removes outlier values from the underlying table. Note that outliers can only be removed for * variables that have a numerical value type ({@code ValueType#isNumeric()} returns true). * * @see OutlierRemovingVariableValueSource */ public class OutlierRemovingView extends View { private final WrappingFunction function = new WrappingFunction(); private final DescriptiveStatisticsProvider statisticsProvider; /** * A cache of {@code OutlierRemovingVariableValueSource}. This holds an instance of {@code * OutlierRemovingVariableValueSource} for each {@code VariableValueSource} in the wrapped table. Note that this cache * is lazily constructed. */ private final Map<String, OutlierRemovingVariableValueSource> sources = Maps.newHashMap(); public OutlierRemovingView() { this(new ExcludeMissingDescriptiveStatisticsProvider()); } public OutlierRemovingView(DescriptiveStatisticsProvider statisticsProvider) { if(statisticsProvider == null) throw new IllegalArgumentException("statisticsProvider cannot be null"); this.statisticsProvider = statisticsProvider; } @NotNull @Override public BijectiveFunction<VariableValueSource, VariableValueSource> getVariableValueSourceMappingFunction() { return function; } /** * Returns true when the {@code VariableValueSource} is a candidate for removing outliers. This method tests that the * source can provide a non-null {@code VectorSource}, that the variable is not repeatable and that its {@code * ValueType} is numeric. * * @param source * @return */ protected boolean canRemoveOutliers(@Nullable VariableValueSource source) { return source != null && source.supportVectorSource() // && !source.getVariable().isRepeatable() // && source.getValueType().isNumeric(); } /** * Lookup a {@code OutlierRemovingVariableValueSource} for the corresponding {@code VariableValueSource}, if an entry * does not exist, a new one is created and returned. * * @param from * @return */ protected synchronized OutlierRemovingVariableValueSource cacheLookup(@Nullable VariableValueSource from) { String variableName = from == null ? null : from.getVariable().getName(); OutlierRemovingVariableValueSource source = variableName == null ? null : sources.get(variableName); if(source == null) { source = new OutlierRemovingVariableValueSource(getWrappedValueTable(), from, statisticsProvider) { @NotNull @Override public Value getValue(ValueSet valueSet) { return super.getValue(getValueSetMappingFunction().unapply(valueSet)); } }; sources.put(variableName, source); } return source; } private final class WrappingFunction implements BijectiveFunction<VariableValueSource, VariableValueSource> { @Override public VariableValueSource unapply(VariableValueSource from) { return ((VariableValueSourceWrapper) from).getWrapped(); } @Override public VariableValueSource apply(VariableValueSource from) { return canRemoveOutliers(from) ? cacheLookup(from) : new ViewVariableValueSource(from); } } }