Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Analysis DGM tests and more #60

Merged
merged 6 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading