Skip to content

Commit

Permalink
Merge pull request #60 from TheD2Lab/testing
Browse files Browse the repository at this point in the history
Analysis DGM tests and more
  • Loading branch information
ashkjones authored Aug 20, 2024
2 parents c38282f + c76e675 commit 8cd1d40
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 33 deletions.
4 changes: 2 additions & 2 deletions src/main/java/com/github/thed2lab/analysis/Analysis.java
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,9 @@ private static List<List<String>> generateResultsHelper(DataEntry allGaze, DataE
resultsMap.putAll(Angles.analyze(validAreaFixations));
resultsMap.putAll(ConvexHull.analyze(validAreaFixations));
resultsMap.putAll(GazeEntropy.analyze(validAreaFixations));
resultsMap.putAll(Blinks.analyze(areaGaze));
resultsMap.putAll(Blinks.analyze(areaGaze)); // unfiltered
resultsMap.putAll(Gaze.analyze(validAreaGaze));
resultsMap.putAll(Event.analyze(validAreaGaze));
resultsMap.putAll(Event.analyze(areaGaze)); // unfiltered

var resultsList = new ArrayList<List<String>>(2);
resultsList.add(new ArrayList<>(resultsMap.keySet()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
import static com.github.thed2lab.analysis.Constants.AOI_LABEL;
import static com.github.thed2lab.analysis.Constants.FIXATION_DURATION;
import static com.github.thed2lab.analysis.Constants.FIXATION_ID;
import static com.github.thed2lab.analysis.Constants.SCREEN_HEIGHT;
import static com.github.thed2lab.analysis.Constants.SCREEN_WIDTH;

import java.util.ArrayList;
import java.util.Arrays;
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/github/thed2lab/analysis/ConvexHull.java
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ protected static Point2D.Double getLowestPoint(List<Point2D.Double> points) {
/**
* Returns a sorted set of points from the list <code>points</code>. The
* set of points are sorted in increasing order of the angle they and the
* lowest point <tt>P</tt> make with the x-axis. If tow (or more) points
* lowest point <tt>P</tt> make with the x-axis. If two (or more) points
* form the same angle towards <tt>P</tt>, the one closest to <tt>P</tt>
* comes first.
*
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/github/thed2lab/analysis/DataEntry.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.github.thed2lab.analysis;

import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

Expand Down
6 changes: 5 additions & 1 deletion src/main/java/com/github/thed2lab/analysis/GazeEntropy.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.github.thed2lab.analysis;

import static com.github.thed2lab.analysis.Constants.AOI_LABEL;
import static com.github.thed2lab.analysis.Constants.FIXATION_ID;

import java.util.ArrayList;
import java.util.HashMap;
Expand All @@ -22,20 +23,23 @@ static public LinkedHashMap<String,String> analyze(DataEntry fixations) {
var transitionProbability = new HashMap<String, Map<String, Double>>();
var aoiSequence = new ArrayList<String>();
String lastAoi = null;
int lastId = -1; // arbitrary number that will never be the ID

int fixationCount = fixations.rowCount();

for (int row = 0; row < fixations.rowCount(); row++) {
String aoi = fixations.getValue(AOI_LABEL, row);
int curId = Integer.valueOf(fixations.getValue(FIXATION_ID, row));
aoiSequence.add(aoi);
aoiProbability.put(aoi, aoiProbability.getOrDefault(aoi, 0.0) + 1);
if (lastAoi != null) { // skips the first loop
if (lastAoi != null && curId == lastId + 1) { // skips the first loop and non-consecutive fixations
Map<String, Double> relationMatrix = transitionProbability.getOrDefault(lastAoi, new HashMap<String,Double>());
double count = relationMatrix.getOrDefault(aoi, 0.0);
relationMatrix.put(aoi, count + 1);
transitionProbability.put(lastAoi, relationMatrix);
}
lastAoi = aoi;
lastId = curId;
}

for (Map.Entry<String, Double> entry : aoiProbability.entrySet()) {
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/com/github/thed2lab/analysis/Windows.java
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,11 @@ static void outputWindowFiles(List<DataEntry> windows, double time0, String outp
headers.add("window_duration");
headers.add("initial_seconds_elapsed_since_start");
headers.add("final_seconds_elapsed_since_start");

// In the combined window folder, add headers if there are none
if (allWindowDGMs.size() == 0) {
allWindowDGMs.add(headers);
}

List<String> dgms = results.get(1);
dgms.add(String.valueOf(time1));
Expand All @@ -265,11 +270,6 @@ static void outputWindowFiles(List<DataEntry> windows, double time0, String outp
dgms.add(String.valueOf(initialDuration));
dgms.add(String.valueOf(finalDuration));
allWindowDGMs.add(dgms);

// In the combined window folder, add headers if there are none
if (allWindowDGMs.size() == 0) {
allWindowDGMs.add(headers);
}

FileHandler.writeToCSV(results, windowDirectory, fileName + "_DGMs");
AreaOfInterests.generateAOIs(windowGaze, windowFixations, windowDirectory, fileName);
Expand Down
65 changes: 65 additions & 0 deletions src/test/java/com/github/thed2lab/analysis/AnalysisTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.github.thed2lab.analysis;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import java.io.File;
import java.io.FileReader;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.junit.Test;

import com.opencsv.CSVReader;

public class AnalysisTest {

private final double PRECISION = 0.000000001; // allowable floating point error
private final String ALL_GAZE_PATH = "./src/test/resources/test_all_gaze.csv";
private final DataEntry ALL_GAZE = FileHandler.buildDataEntry(new File(ALL_GAZE_PATH));
private final String ALL_FIXATION_PATH = "./src/test/resources/filtered_by_fixation.csv";
private final DataEntry ALL_FIXATION = FileHandler.buildDataEntry(new File(ALL_FIXATION_PATH));

@Test
public void testGenerateResults_wholeScreenData() {
List<String[]> expected = new LinkedList<>();
try (
FileReader fileReader = new FileReader("./src/test/resources/test_DGMs.csv");
CSVReader csvReader = new CSVReader(fileReader);
) {
expected.add(csvReader.readNext());
expected.add(csvReader.readNext());
} catch (Exception e) {
fail("Could not read file");
}

List<List<String>> actual = Analysis.generateResults(ALL_GAZE, ALL_FIXATION);
assertEquals(2, actual.size()); // headers and 1 row of data

// check headers
Iterator<String> actualIter = actual.get(0).iterator();
for (String expValue : expected.get(0)) {
assertEquals(expValue.trim(), actualIter.next());
}

// check values
actualIter = actual.get(1).iterator();
for (String expValue : expected.get(1)) {
var bloop = actualIter.next();
if (!isEqual(expValue, bloop)) {
fail();
}
}
}

/**
* Checks if two numbers saved as strings are equal, within an allowable amount of floating point error.
* @param double1 first number to compare
* @param double2 second number to compare
* @return if the two numbers are equal
*/
private boolean isEqual(String double1, String double2) {
return Math.abs(Double.parseDouble(double1) - Double.parseDouble(double2)) < PRECISION;
}
}
68 changes: 47 additions & 21 deletions src/test/java/com/github/thed2lab/analysis/GazeEntropyTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ public class GazeEntropyTest {
public void testGazeEntropyAnalyze_singleAoi_0entropy() {
final double EXPECTED_STATIONARY = 0.0;
final double EXPECTED_TRANSITION = 0.0;
DataEntry data = new DataEntry(Arrays.asList("AOI")) {{
process(Arrays.asList("A"));
process(Arrays.asList("A"));
process(Arrays.asList("A"));
process(Arrays.asList("A"));
DataEntry data = new DataEntry(Arrays.asList("FPOGID","AOI")) {{
process(Arrays.asList("0", "A"));
process(Arrays.asList("1", "A"));
process(Arrays.asList("1", "A"));
process(Arrays.asList("1", "A"));
}};
var results = GazeEntropy.analyze(data);
assertEquals(
Expand All @@ -40,15 +40,15 @@ public void testGazeEntropyAnalyze_singleAoi_0entropy() {
public void testGazeEntropyAnalyze_threeAoi() {
final double EXPECTED_STATIONARY = 0.4699915470362;
final double EXPECTED_TRANSITION = 0.282583442123752;
DataEntry data = new DataEntry(Arrays.asList("AOI")) {{
process(Arrays.asList("A"));
process(Arrays.asList("B"));
process(Arrays.asList("B"));
process(Arrays.asList("A"));
process(Arrays.asList("A"));
process(Arrays.asList("B"));
process(Arrays.asList("C"));
process(Arrays.asList("C"));
DataEntry data = new DataEntry(Arrays.asList("FPOGID", "AOI")) {{
process(Arrays.asList("1", "A"));
process(Arrays.asList("2", "B"));
process(Arrays.asList("3", "B"));
process(Arrays.asList("4", "A"));
process(Arrays.asList("5", "A"));
process(Arrays.asList("6", "B"));
process(Arrays.asList("7", "C"));
process(Arrays.asList("8", "C"));
}};
var results = GazeEntropy.analyze(data);
assertEquals(
Expand All @@ -68,13 +68,39 @@ public void testGazeEntropyAnalyze_threeAoi() {
public void testGazeEntropyAnalyze_undefinedAoi() {
final double EXPECTED_STATIONARY = 0.301029995663981;
final double EXPECTED_TRANSITION = 0.288732293303828;
DataEntry data = new DataEntry(Arrays.asList("AOI")) {{
process(Arrays.asList("A"));
process(Arrays.asList(""));
process(Arrays.asList(""));
process(Arrays.asList("A"));
process(Arrays.asList("A"));
process(Arrays.asList(""));
DataEntry data = new DataEntry(Arrays.asList("FPOGID", "AOI")) {{
process(Arrays.asList("0", "A"));
process(Arrays.asList("1", ""));
process(Arrays.asList("2", ""));
process(Arrays.asList("3", "A"));
process(Arrays.asList("4", "A"));
process(Arrays.asList("5", ""));
}};
var results = GazeEntropy.analyze(data);
assertEquals(
"Unexpected stationary entropy.",
EXPECTED_STATIONARY,
Double.parseDouble(results.get(STATIONARY_ENTROPY)),
PRECISION
);
assertEquals(
"Unexpected transition entropy.",
EXPECTED_TRANSITION, Double.parseDouble(results.get(TRANSITION_ENTROPY)),
PRECISION
);
}

@Test
public void testGazeEntropyAnalyze_nonConsecutiveFixations() {
final double EXPECTED_STATIONARY = 0.301029995663981;
final double EXPECTED_TRANSITION = 0.138217295471837;
DataEntry data = new DataEntry(Arrays.asList("FPOGID", "AOI")) {{
process(Arrays.asList("0", "A"));
process(Arrays.asList("1", ""));
process(Arrays.asList("2", ""));
process(Arrays.asList("4", "A"));
process(Arrays.asList("5", "A"));
process(Arrays.asList("6", ""));
}};
var results = GazeEntropy.analyze(data);
assertEquals(
Expand Down
2 changes: 2 additions & 0 deletions src/test/resources/test_DGMs.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
total_number_of_fixations,sum_of_all_fixation_duration_s,mean_fixation_duration_s,median_fixation_duration_s,stdev_of_fixation_durations_s,min_fixation_duration_s,max_fixation_duration_s,total_number_of_saccades,sum_of_all_saccade_lengths,mean_saccade_length,median_saccade_length,stdev_of_saccade_lengths,min_saccade_length,max_saccade_length,sum_of_all_saccade_durations,mean_saccade_duration,median_saccade_duration,stdev_of_saccade_durations,min_saccade_duration,max_saccade_duration,scanpath_duration,fixation_to_saccade_ratio,average_peak_saccade_velocity,sum_of_all_absolute_degrees,mean_absolute_degree,median_absolute_degree,stdev_of_absolute_degrees,min_absolute_degree,max_absolute_degree,sum_of_all_relative_degrees,mean_relative_degree,median_relative_degree,stdev_of_relative_degrees,min_relative_degree,max_relative_degree,convex_hull_area,stationary_entropy,transition_entropy,total_number_of_blinks,average_blink_rate_per_minute,total_number_of_valid_recordings,average_pupil_size_of_left_eye,average_pupil_size_of_right_eye,average_pupil_size_of_both_eyes,total_number_of_l_mouse_clicks
20,4.76282,0.238141,0.16748,0.152435548482145,0.08044,0.66125,16,2530.86608142828,158.179130089267,119.346748844631,106.3517507880460,20.7083517064975,349.414837212159,0.19091,0.011931875,0.00671,0.0094964149156,0.00598,0.03028,4.95373,24.9479859619718,179.969192732729,395.580356733446,24.7237722958404,15.4757613889113,22.7925364835493,2.9677710724739,81.4803527675405,1618.85106640278,124.527005107906,123.297847178851,26.0218488445123,79.5855258680609,163.151086040417,168928.54539264,0.3482253728572,0.2295311788752,3,27.5902238640109,822,4.5235607299270,4.3886963746959,4.4561285523114,2

0 comments on commit 8cd1d40

Please sign in to comment.