1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  package ubic.basecode.util.r;
21  
22  import org.apache.commons.lang3.StringUtils;
23  import org.jspecify.annotations.Nullable;
24  import org.rosuda.REngine.REXP;
25  import org.rosuda.REngine.REXPMismatchException;
26  import ubic.basecode.math.linearmodels.AnovaEffect;
27  import ubic.basecode.math.linearmodels.GenericAnovaResult;
28  import ubic.basecode.math.linearmodels.LinearModelSummary;
29  
30  import java.util.*;
31  import java.util.stream.Collectors;
32  
33  
34  
35  
36  
37  
38  class GenericAnovaResultImpl extends AbstractAnovaResult implements GenericAnovaResult {
39  
40      private static final long serialVersionUID = 1L;
41  
42      private final Map<TreeSet<String>, AnovaEffect> interactionEffects = new LinkedHashMap<>();
43      private final Map<String, AnovaEffect> mainEffects = new LinkedHashMap<>();
44      @Nullable
45      private final AnovaEffect residual;
46  
47      
48  
49  
50      public GenericAnovaResultImpl( String key, REXP rAnovaTable ) throws REXPMismatchException {
51          super( key );
52          String[] names = rAnovaTable.getAttribute( "row.names" ).asStrings();
53          double[] pvs = rAnovaTable.asList().at( "Pr(>F)" ).asDoubles();
54          double[] dfs = rAnovaTable.asList().at( "Df" ).asDoubles();
55          double[] fs = rAnovaTable.asList().at( "F value" ).asDoubles();
56          double[] ssq = rAnovaTable.asList().at( "Sum Sq" ).asDoubles();
57          AnovaEffect residual = null;
58          for ( int i = 0; i < pvs.length; i++ ) {
59              String termLabel = names[i];
60              boolean isInteraction = termLabel.contains( ":" );
61              boolean isResidual = termLabel.equals( "Residuals" );
62              AnovaEffect ae = new AnovaEffect( termLabel, pvs[i], fs[i], dfs[i], ssq[i], isInteraction, isResidual );
63              if ( isInteraction ) { 
64                  interactionEffects.put( new TreeSet<>( Arrays.asList( StringUtils.split( termLabel, ":" ) ) ), ae );
65              } else if ( isResidual ) {
66                  residual = ae;
67              } else {
68                  mainEffects.put( termLabel, ae );
69              }
70          }
71          this.residual = residual;
72      }
73  
74      @Override
75      public boolean hasResiduals() {
76          return residual != null;
77      }
78  
79      @Override
80      public double getResidualsDof() {
81          return residual != null ? residual.getDof() : Double.NaN;
82      }
83  
84      @Override
85      public double getResidualsFStat() {
86          return residual != null ? residual.getFStat() : Double.NaN;
87      }
88  
89      @Override
90      public double getResidualsPValue() {
91          return residual != null ? residual.getPValue() : Double.NaN;
92      }
93  
94      @Override
95      public double getInteractionEffectDof() {
96          if ( interactionEffects.size() > 1 ) {
97              throw new IllegalArgumentException( "Must specify which interaction" );
98          }
99          if ( interactionEffects.isEmpty() ) {
100             return Double.NaN;
101         }
102         return interactionEffects.values().iterator().next().getDof();
103     }
104 
105     @Override
106     public double getInteractionEffectFStat() {
107         if ( interactionEffects.size() > 1 ) {
108             throw new IllegalArgumentException( "Must specify which interaction" );
109         }
110         if ( interactionEffects.isEmpty() ) {
111             return Double.NaN;
112         }
113         return interactionEffects.values().iterator().next().getFStat();
114     }
115 
116     @Override
117     public double getInteractionEffectPValue() {
118         if ( interactionEffects.size() > 1 ) {
119             throw new IllegalArgumentException( "Must specify which interaction" );
120         }
121         if ( interactionEffects.isEmpty() ) {
122             return Double.NaN;
123         }
124         return interactionEffects.values().iterator().next().getPValue();
125     }
126 
127     @Override
128     public double getInteractionEffectDof( String... factorNames ) {
129         if ( interactionEffects.isEmpty() ) {
130             return Double.NaN;
131         }
132         TreeSet<String> interactionFactor = new TreeSet<>( Arrays.asList( factorNames ) );
133         if ( !interactionEffects.containsKey( interactionFactor ) ) {
134             return Double.NaN;
135         }
136         return interactionEffects.get( interactionFactor ).getDof();
137     }
138 
139     @Override
140     public double getInteractionEffectFStat( String... factorNames ) {
141         if ( interactionEffects.isEmpty() ) {
142             return Double.NaN;
143         }
144         TreeSet<String> interactionFactor = new TreeSet<>( Arrays.asList( factorNames ) );
145         if ( !interactionEffects.containsKey( interactionFactor ) ) {
146             return Double.NaN;
147         }
148         return interactionEffects.get( interactionFactor ).getFStat();
149     }
150 
151     
152 
153 
154     @Override
155     public double getInteractionEffectPValue( String... factorNames ) {
156         TreeSet<String> interactionFactor = new TreeSet<>( Arrays.asList( factorNames ) );
157         if ( interactionEffects.isEmpty() ) {
158             return Double.NaN;
159         }
160         if ( !interactionEffects.containsKey( interactionFactor ) ) {
161             return Double.NaN;
162         }
163         return interactionEffects.get( interactionFactor ).getPValue();
164     }
165 
166     @Override
167     public double getMainEffectDof( String factorName ) {
168         if ( mainEffects.get( factorName ) == null ) {
169             return Double.NaN;
170         }
171         return mainEffects.get( factorName ).getDof();
172     }
173 
174     @Override
175     public double getMainEffectFStat( String factorName ) {
176         if ( mainEffects.get( factorName ) == null ) {
177             return Double.NaN;
178         }
179         return mainEffects.get( factorName ).getFStat();
180     }
181 
182     
183 
184 
185     @Override
186     public Collection<String> getMainEffectFactorNames() {
187         return mainEffects.keySet();
188     }
189 
190     @Override
191     public double getMainEffectPValue( String factorName ) {
192         if ( mainEffects.get( factorName ) == null ) {
193             return Double.NaN;
194         }
195         return mainEffects.get( factorName ).getPValue();
196     }
197 
198     @Override
199     public Collection<String[]> getInteractionEffectFactorNames() {
200         return interactionEffects.keySet().stream()
201             .map( fn -> fn.toArray( new String[0] ) )
202             .collect( Collectors.toList() );
203     }
204 
205     @Override
206     public boolean hasInteractions() {
207         return !interactionEffects.isEmpty();
208     }
209 
210     @Override
211     public String toString() {
212         StringBuilder buf = new StringBuilder();
213 
214         buf.append( "ANOVA table " ).append( this.getKey() ).append( " \n" );
215 
216         buf.append( StringUtils.leftPad( "\t", 10 ) ).append( "Df\tSSq\tMSq\tF\tP\n" );
217 
218         for ( String me : this.getMainEffectFactorNames() ) {
219             if ( me.equals( LinearModelSummary.INTERCEPT_COEFFICIENT_NAME ) ) {
220                 continue;
221             }
222             AnovaEffect a = mainEffects.get( me );
223             buf.append( a ).append( "\n" );
224         }
225 
226         if ( hasInteractions() ) {
227             for ( TreeSet<String> ifa : interactionEffects.keySet() ) {
228                 AnovaEffect a = this.interactionEffects.get( ifa );
229                 buf.append( a ).append( "\n" );
230             }
231         }
232 
233         buf.append( residual ).append( "\n" );
234 
235         return buf.toString();
236     }
237 }