From 57097af1e95b62977ca7d3943fe4c33761c7b5f3 Mon Sep 17 00:00:00 2001 From: ashkjones Date: Wed, 31 Jul 2024 14:19:41 -0700 Subject: [PATCH 1/5] Fixed transition entropy for 2 AOI where all fixation occur in AOIs --- .../github/thed2lab/analysis/GazeEntropy.java | 69 +++++++++++-------- .../thed2lab/analysis/GazeEntropyTest.java | 41 +++++++++++ 2 files changed, 80 insertions(+), 30 deletions(-) create mode 100644 src/test/java/com/github/thed2lab/analysis/GazeEntropyTest.java diff --git a/src/main/java/com/github/thed2lab/analysis/GazeEntropy.java b/src/main/java/com/github/thed2lab/analysis/GazeEntropy.java index ede3d95..7097a39 100644 --- a/src/main/java/com/github/thed2lab/analysis/GazeEntropy.java +++ b/src/main/java/com/github/thed2lab/analysis/GazeEntropy.java @@ -6,40 +6,35 @@ import java.util.Map; public class GazeEntropy { - final static String AOI_INDEX = "AOI"; + private final static String AOI_INDEX = "AOI"; - static public LinkedHashMap analyze(DataEntry data) { - LinkedHashMap results = new LinkedHashMap(); - - HashMap aoiProbability = new HashMap(); - HashMap> transitionProbability = new HashMap>(); - ArrayList aoiSequence = new ArrayList(); + /** + * Iterates over all rows of a participant’s fixations and calculates the probability of transition to each AOI, as well as calculating + * the probability that, given a fixation is in some AOIA, the probability of transitioning to some AOIB. + * @param fixations the users gaze data, filtered by fixations and validity with screen size applied + * @return a map with the key as the outputted variable’s name and the value as a string containing the result + */ + static public LinkedHashMap analyze(DataEntry fixations) { + var aoiProbability = new HashMap(); + var transitionProbability = new HashMap>(); + var aoiSequence = new ArrayList(); String lastAoi = ""; - int fixationCount = data.rowCount(); + int fixationCount = fixations.rowCount(); - for (int row = 0; row < data.rowCount(); row++) { - String aoi = data.getValue(AOI_INDEX, row); + for (int row = 0; row < fixations.rowCount(); row++) { + String aoi = fixations.getValue(AOI_INDEX, row); aoiSequence.add(aoi); if (aoi.equals("")) continue; - else if (aoiProbability.containsKey(aoi)) { - aoiProbability.put(aoi, aoiProbability.get(aoi) + 1); - if (!lastAoi.equals("")) { - HashMap relationMatrix = transitionProbability.get(lastAoi); - if (relationMatrix.containsKey(aoi)) { - double count = relationMatrix.get(aoi); - relationMatrix.put(aoi, count + 1); - } else { - relationMatrix.put(aoi, 1.0); - } - } - - } else { - aoiProbability.put(aoi, 1.0); - transitionProbability.put(aoi, new HashMap()); - } + aoiProbability.put(aoi, aoiProbability.getOrDefault(aoi, 0.0) + 1); + if (!lastAoi.equals("")) { + Map relationMatrix = transitionProbability.getOrDefault(lastAoi, new HashMap()); + double count = relationMatrix.getOrDefault(aoi, 0.0); + relationMatrix.put(aoi, count + 1); + transitionProbability.put(lastAoi, relationMatrix); + } lastAoi = aoi; } @@ -50,7 +45,7 @@ else if (aoiProbability.containsKey(aoi)) { } - for (Map.Entry> entry : transitionProbability.entrySet()) { + for (Map.Entry> entry : transitionProbability.entrySet()) { int aoiTransitions = 0; for (Map.Entry edge : entry.getValue().entrySet()) { aoiTransitions += edge.getValue(); @@ -60,6 +55,8 @@ else if (aoiProbability.containsKey(aoi)) { } } + var results = new LinkedHashMap(); + results.put( "stationary_entropy", //Output Header String.valueOf(getStationaryEntropy(aoiProbability)) //Output Value @@ -73,7 +70,12 @@ else if (aoiProbability.containsKey(aoi)) { return results; } - public static double getStationaryEntropy(HashMap aoiProbability) { + /** + * Calculates the stationary entropy score + * @param aoiProbability a map between an AOI name and probability that any given transition is to that AOI + * @return the stationary entropy score + */ + static double getStationaryEntropy(Map aoiProbability) { double stationaryEntropy = 0; for (Map.Entry entry : aoiProbability.entrySet()) { double probability = entry.getValue(); @@ -83,9 +85,16 @@ public static double getStationaryEntropy(HashMap aoiProbability return stationaryEntropy; } - public static double getTransitionEntropy(HashMap aoiProbability, HashMap> transitionMatrix){ + /** + * Calculates the transition entropy score. + * @param aoiProbability the AOI name mapped to the probability that a transition is to that AOI + * @param transitionMatrix the outer matrix has a key for each AOI (A). The value for each AOI is another Hashmap containing all AOIs (inclusive) (B). + * The value for each of those is the probability of transitioning from A to B. + * @return the stationary entropy score + */ + static double getTransitionEntropy(Map aoiProbability, Map> transitionMatrix){ double transitionEntropy = 0; - for (Map.Entry> entry : transitionMatrix.entrySet()) { + for (Map.Entry> entry : transitionMatrix.entrySet()) { double pijSum = 0; for (Map.Entry edge : entry.getValue().entrySet()) { pijSum += edge.getValue() * Math.log10(edge.getValue()); diff --git a/src/test/java/com/github/thed2lab/analysis/GazeEntropyTest.java b/src/test/java/com/github/thed2lab/analysis/GazeEntropyTest.java new file mode 100644 index 0000000..c78ff20 --- /dev/null +++ b/src/test/java/com/github/thed2lab/analysis/GazeEntropyTest.java @@ -0,0 +1,41 @@ +package com.github.thed2lab.analysis; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; + +import org.junit.Test; + +public class GazeEntropyTest { + + private final double PRECISION = 0.000000001; // allowable floating point error + private final String STATIONARY_ENTROPY = "stationary_entropy"; + private final String TRANSITION_ENTROPY = "transition_entropy"; + + + @Test + public void testAnalyze_twoAoi() { + 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("B")); + process(Arrays.asList("B")); + process(Arrays.asList("A")); + process(Arrays.asList("A")); + process(Arrays.asList("B")); + }}; + 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 + ); + } +} From 3f0bacb5bad6b02ea2beecc9441cc000554136a9 Mon Sep 17 00:00:00 2001 From: ashkjones Date: Wed, 31 Jul 2024 14:19:41 -0700 Subject: [PATCH 2/5] Fixed transition entropy for 2 AOI where all fixation occur in AOIs --- .../github/thed2lab/analysis/GazeEntropy.java | 69 +++++++++++-------- .../thed2lab/analysis/GazeEntropyTest.java | 41 +++++++++++ 2 files changed, 80 insertions(+), 30 deletions(-) create mode 100644 src/test/java/com/github/thed2lab/analysis/GazeEntropyTest.java diff --git a/src/main/java/com/github/thed2lab/analysis/GazeEntropy.java b/src/main/java/com/github/thed2lab/analysis/GazeEntropy.java index ede3d95..7097a39 100644 --- a/src/main/java/com/github/thed2lab/analysis/GazeEntropy.java +++ b/src/main/java/com/github/thed2lab/analysis/GazeEntropy.java @@ -6,40 +6,35 @@ import java.util.Map; public class GazeEntropy { - final static String AOI_INDEX = "AOI"; + private final static String AOI_INDEX = "AOI"; - static public LinkedHashMap analyze(DataEntry data) { - LinkedHashMap results = new LinkedHashMap(); - - HashMap aoiProbability = new HashMap(); - HashMap> transitionProbability = new HashMap>(); - ArrayList aoiSequence = new ArrayList(); + /** + * Iterates over all rows of a participant’s fixations and calculates the probability of transition to each AOI, as well as calculating + * the probability that, given a fixation is in some AOIA, the probability of transitioning to some AOIB. + * @param fixations the users gaze data, filtered by fixations and validity with screen size applied + * @return a map with the key as the outputted variable’s name and the value as a string containing the result + */ + static public LinkedHashMap analyze(DataEntry fixations) { + var aoiProbability = new HashMap(); + var transitionProbability = new HashMap>(); + var aoiSequence = new ArrayList(); String lastAoi = ""; - int fixationCount = data.rowCount(); + int fixationCount = fixations.rowCount(); - for (int row = 0; row < data.rowCount(); row++) { - String aoi = data.getValue(AOI_INDEX, row); + for (int row = 0; row < fixations.rowCount(); row++) { + String aoi = fixations.getValue(AOI_INDEX, row); aoiSequence.add(aoi); if (aoi.equals("")) continue; - else if (aoiProbability.containsKey(aoi)) { - aoiProbability.put(aoi, aoiProbability.get(aoi) + 1); - if (!lastAoi.equals("")) { - HashMap relationMatrix = transitionProbability.get(lastAoi); - if (relationMatrix.containsKey(aoi)) { - double count = relationMatrix.get(aoi); - relationMatrix.put(aoi, count + 1); - } else { - relationMatrix.put(aoi, 1.0); - } - } - - } else { - aoiProbability.put(aoi, 1.0); - transitionProbability.put(aoi, new HashMap()); - } + aoiProbability.put(aoi, aoiProbability.getOrDefault(aoi, 0.0) + 1); + if (!lastAoi.equals("")) { + Map relationMatrix = transitionProbability.getOrDefault(lastAoi, new HashMap()); + double count = relationMatrix.getOrDefault(aoi, 0.0); + relationMatrix.put(aoi, count + 1); + transitionProbability.put(lastAoi, relationMatrix); + } lastAoi = aoi; } @@ -50,7 +45,7 @@ else if (aoiProbability.containsKey(aoi)) { } - for (Map.Entry> entry : transitionProbability.entrySet()) { + for (Map.Entry> entry : transitionProbability.entrySet()) { int aoiTransitions = 0; for (Map.Entry edge : entry.getValue().entrySet()) { aoiTransitions += edge.getValue(); @@ -60,6 +55,8 @@ else if (aoiProbability.containsKey(aoi)) { } } + var results = new LinkedHashMap(); + results.put( "stationary_entropy", //Output Header String.valueOf(getStationaryEntropy(aoiProbability)) //Output Value @@ -73,7 +70,12 @@ else if (aoiProbability.containsKey(aoi)) { return results; } - public static double getStationaryEntropy(HashMap aoiProbability) { + /** + * Calculates the stationary entropy score + * @param aoiProbability a map between an AOI name and probability that any given transition is to that AOI + * @return the stationary entropy score + */ + static double getStationaryEntropy(Map aoiProbability) { double stationaryEntropy = 0; for (Map.Entry entry : aoiProbability.entrySet()) { double probability = entry.getValue(); @@ -83,9 +85,16 @@ public static double getStationaryEntropy(HashMap aoiProbability return stationaryEntropy; } - public static double getTransitionEntropy(HashMap aoiProbability, HashMap> transitionMatrix){ + /** + * Calculates the transition entropy score. + * @param aoiProbability the AOI name mapped to the probability that a transition is to that AOI + * @param transitionMatrix the outer matrix has a key for each AOI (A). The value for each AOI is another Hashmap containing all AOIs (inclusive) (B). + * The value for each of those is the probability of transitioning from A to B. + * @return the stationary entropy score + */ + static double getTransitionEntropy(Map aoiProbability, Map> transitionMatrix){ double transitionEntropy = 0; - for (Map.Entry> entry : transitionMatrix.entrySet()) { + for (Map.Entry> entry : transitionMatrix.entrySet()) { double pijSum = 0; for (Map.Entry edge : entry.getValue().entrySet()) { pijSum += edge.getValue() * Math.log10(edge.getValue()); diff --git a/src/test/java/com/github/thed2lab/analysis/GazeEntropyTest.java b/src/test/java/com/github/thed2lab/analysis/GazeEntropyTest.java new file mode 100644 index 0000000..c78ff20 --- /dev/null +++ b/src/test/java/com/github/thed2lab/analysis/GazeEntropyTest.java @@ -0,0 +1,41 @@ +package com.github.thed2lab.analysis; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; + +import org.junit.Test; + +public class GazeEntropyTest { + + private final double PRECISION = 0.000000001; // allowable floating point error + private final String STATIONARY_ENTROPY = "stationary_entropy"; + private final String TRANSITION_ENTROPY = "transition_entropy"; + + + @Test + public void testAnalyze_twoAoi() { + 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("B")); + process(Arrays.asList("B")); + process(Arrays.asList("A")); + process(Arrays.asList("A")); + process(Arrays.asList("B")); + }}; + 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 + ); + } +} From 0d800b81f1c882c8bc33ffcd84d0854b9602877f Mon Sep 17 00:00:00 2001 From: ashkjones Date: Wed, 7 Aug 2024 12:00:54 -0700 Subject: [PATCH 3/5] Fix gaze entropy and add tests relates to #51 and #52. Data reocords with no AOI value are treated as though they belong to an AOI ("Undefined Area") in the entropy calcs. No Aoi is renamed to "Undefined Area" because now it is an AOI in a sense --- .../thed2lab/analysis/AreaOfInterests.java | 8 +-- .../github/thed2lab/analysis/GazeEntropy.java | 34 +++++------ .../github/thed2lab/analysis/Sequences.java | 4 +- .../thed2lab/analysis/GazeEntropyTest.java | 59 +++++++++++++++++-- 4 files changed, 78 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/github/thed2lab/analysis/AreaOfInterests.java b/src/main/java/com/github/thed2lab/analysis/AreaOfInterests.java index 8d3192b..999d81d 100644 --- a/src/main/java/com/github/thed2lab/analysis/AreaOfInterests.java +++ b/src/main/java/com/github/thed2lab/analysis/AreaOfInterests.java @@ -20,7 +20,7 @@ public static void generateAOIs(DataEntry allGazeData, String outputDirectory, S LinkedHashMap aoiMetrics = new LinkedHashMap<>(); for (int i = 0; i < allGazeData.rowCount(); i++) { String aoi = allGazeData.getValue(AOI_INDEX, i); - String aoiKey = aoi.equals("") ? "No AOI" : aoi; + String aoiKey = aoi.equals("") ? "Undefined Area" : aoi; if (!aoiMetrics.containsKey(aoiKey)) { DataEntry d = new DataEntry(allGazeData.getHeaders()); aoiMetrics.put(aoiKey, d); @@ -34,7 +34,7 @@ public static void generateAOIs(DataEntry allGazeData, String outputDirectory, S //System.out.println(allFixations.rowCount()); for (int i = 0; i < allFixations.rowCount(); i++) { String aoi = allFixations.getValue(AOI_INDEX, i); - String aoiKey = aoi.equals("") ? "No AOI" : aoi; + String aoiKey = aoi.equals("") ? "Undefined Area" : aoi; if (!aoiFixationMetrics.containsKey(aoiKey)) { DataEntry d = new DataEntry(allFixations.getHeaders()); aoiFixationMetrics.put(aoiKey, d); @@ -113,10 +113,10 @@ public static ArrayList> generatePairResults(DataEntry fixations, L LinkedHashMap> transitionCounts = new LinkedHashMap<>(); for (int i = 0; i < fixations.rowCount()-1; i++) { String curAoi = fixations.getValue(AOI_INDEX, i); - curAoi = curAoi.equals("") ? "No AOI" : curAoi; + curAoi = curAoi.equals("") ? "Undefined Area" : curAoi; int curId = Integer.valueOf(fixations.getValue(FIXATIONID_INDEX, i)); String nextAoi = fixations.getValue(AOI_INDEX, i+1); - nextAoi = nextAoi.equals("") ? "No AOI" : nextAoi; + nextAoi = nextAoi.equals("") ? "Undefined Area" : nextAoi; int nextId = Integer.valueOf(fixations.getValue(FIXATIONID_INDEX, i+1)); boolean isValidAOI = (validAOIs.containsKey(curAoi) && validAOIs.containsKey(nextAoi)); if (isValidAOI && nextId == curId + 1) { //Check if fixations are subsequent diff --git a/src/main/java/com/github/thed2lab/analysis/GazeEntropy.java b/src/main/java/com/github/thed2lab/analysis/GazeEntropy.java index 7097a39..1e1f4ad 100644 --- a/src/main/java/com/github/thed2lab/analysis/GazeEntropy.java +++ b/src/main/java/com/github/thed2lab/analysis/GazeEntropy.java @@ -9,32 +9,31 @@ public class GazeEntropy { private final static String AOI_INDEX = "AOI"; /** - * Iterates over all rows of a participant’s fixations and calculates the probability of transition to each AOI, as well as calculating - * the probability that, given a fixation is in some AOIA, the probability of transitioning to some AOIB. - * @param fixations the users gaze data, filtered by fixations and validity with screen size applied - * @return a map with the key as the outputted variable’s name and the value as a string containing the result + * Calculates the stationary entropy and transition entropy measures. The method iterates over the participant’s + * fixations, determining the probability of viewing each AOI and the probability of transitioning from one AOI + * to another. All lines of data not labeled with an AOI are treated as if they appear in a single AOI. + * Therefore, n in the entropy formula will be the total number of labeled AOIs plus one. . + * @param fixations the users gaze data, filtered by fixations and validity with screen size applied. + * @return an ordered map containing the stationary and transition entropy headers as keys mapped to their computed values. */ static public LinkedHashMap analyze(DataEntry fixations) { var aoiProbability = new HashMap(); var transitionProbability = new HashMap>(); var aoiSequence = new ArrayList(); - String lastAoi = ""; + String lastAoi = null; int fixationCount = fixations.rowCount(); for (int row = 0; row < fixations.rowCount(); row++) { String aoi = fixations.getValue(AOI_INDEX, row); aoiSequence.add(aoi); - - if (aoi.equals("")) - continue; aoiProbability.put(aoi, aoiProbability.getOrDefault(aoi, 0.0) + 1); - if (!lastAoi.equals("")) { + if (lastAoi != null) { // skips the first loop Map relationMatrix = transitionProbability.getOrDefault(lastAoi, new HashMap()); double count = relationMatrix.getOrDefault(aoi, 0.0); relationMatrix.put(aoi, count + 1); transitionProbability.put(lastAoi, relationMatrix); - } + } lastAoi = aoi; } @@ -71,9 +70,9 @@ static public LinkedHashMap analyze(DataEntry fixations) { } /** - * Calculates the stationary entropy score - * @param aoiProbability a map between an AOI name and probability that any given transition is to that AOI - * @return the stationary entropy score + * Calculates the stationary entropy score. + * @param aoiProbability the AOI labels mapped to the probability of viewing the AOI. + * @return the stationary entropy score. */ static double getStationaryEntropy(Map aoiProbability) { double stationaryEntropy = 0; @@ -87,10 +86,11 @@ static double getStationaryEntropy(Map aoiProbability) { /** * Calculates the transition entropy score. - * @param aoiProbability the AOI name mapped to the probability that a transition is to that AOI - * @param transitionMatrix the outer matrix has a key for each AOI (A). The value for each AOI is another Hashmap containing all AOIs (inclusive) (B). - * The value for each of those is the probability of transitioning from A to B. - * @return the stationary entropy score + * @param aoiProbability the AOI labels mapped to the probability of viewing the AOI. + * @param transitionMatrix the outer map has a key for each AOI (A). The value for each AOI is another map + * containing all AOIs (inclusive) (B). The value for each of those is the probability of transitioning + * from A to B. + * @return the stationary entropy score. */ static double getTransitionEntropy(Map aoiProbability, Map> transitionMatrix){ double transitionEntropy = 0; diff --git a/src/main/java/com/github/thed2lab/analysis/Sequences.java b/src/main/java/com/github/thed2lab/analysis/Sequences.java index ebb3379..cc619a6 100644 --- a/src/main/java/com/github/thed2lab/analysis/Sequences.java +++ b/src/main/java/com/github/thed2lab/analysis/Sequences.java @@ -15,7 +15,7 @@ public static void generateSequenceFiles(DataEntry data, String outputDirectory, // Build aoiDescriptions string for (String s: map.keySet()) { int asciiValue = map.get(s); - String description = s == "" ? "No AOI" : s; + String description = s == "" ? "Undefined Area" : s; aoiDescriptions += (char)asciiValue + ", " + description + "\n"; } @@ -26,7 +26,7 @@ public static void generateSequenceFiles(DataEntry data, String outputDirectory, if (!map.containsKey(aoi)) { map.put(aoi, map.size() + ascii); - String description = aoi == "" ? "No AOI" : aoi; + String description = aoi == "" ? "Undefined Area" : aoi; aoiDescriptions += (char)(map.size() + ascii - 1) + ", " + description + "\n"; } diff --git a/src/test/java/com/github/thed2lab/analysis/GazeEntropyTest.java b/src/test/java/com/github/thed2lab/analysis/GazeEntropyTest.java index c78ff20..e58db8d 100644 --- a/src/test/java/com/github/thed2lab/analysis/GazeEntropyTest.java +++ b/src/test/java/com/github/thed2lab/analysis/GazeEntropyTest.java @@ -12,11 +12,34 @@ public class GazeEntropyTest { private final String STATIONARY_ENTROPY = "stationary_entropy"; private final String TRANSITION_ENTROPY = "transition_entropy"; - @Test - public void testAnalyze_twoAoi() { - final double EXPECTED_STATIONARY = 0.301029995663981; - final double EXPECTED_TRANSITION = 0.288732293303828; + 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")); + }}; + 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_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")); @@ -24,6 +47,34 @@ public void testAnalyze_twoAoi() { process(Arrays.asList("A")); process(Arrays.asList("A")); process(Arrays.asList("B")); + process(Arrays.asList("C")); + process(Arrays.asList("C")); + }}; + 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_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("")); }}; var results = GazeEntropy.analyze(data); assertEquals( From 322c9bf341ec5a79e59beac1a5bdd03d6a0c3334 Mon Sep 17 00:00:00 2001 From: ashkjones Date: Wed, 7 Aug 2024 15:05:35 -0700 Subject: [PATCH 4/5] Fixed filtering issue and made a constants class blinks were 0 because filtering was being applied so i reworked analysis class. There was a lot of redundant final constants so I put them in one class so it would be easier to manage. I applied the screen size once and then made an overloaded filterbyvalidity method to support filtering with a screen size --- .../github/thed2lab/analysis/Analysis.java | 57 ++++++++-------- .../com/github/thed2lab/analysis/Angles.java | 13 ++-- .../thed2lab/analysis/AreaOfInterests.java | 43 ++++++------ .../com/github/thed2lab/analysis/Blinks.java | 16 +++-- .../github/thed2lab/analysis/Constants.java | 64 ++++++++++++++++++ .../github/thed2lab/analysis/ConvexHull.java | 9 +-- .../github/thed2lab/analysis/DataFilter.java | 62 ++++++++++------- .../com/github/thed2lab/analysis/Event.java | 5 +- .../github/thed2lab/analysis/Fixations.java | 5 +- .../com/github/thed2lab/analysis/Gaze.java | 8 +-- .../github/thed2lab/analysis/GazeEntropy.java | 5 +- .../github/thed2lab/analysis/Parameters.java | 13 ---- .../github/thed2lab/analysis/Patterns.java | 6 -- .../thed2lab/analysis/SaccadeVelocity.java | 30 ++++----- .../github/thed2lab/analysis/Saccades.java | 25 +++---- .../github/thed2lab/analysis/Sequences.java | 6 +- .../com/github/thed2lab/analysis/Windows.java | 66 +++++++++++-------- 17 files changed, 256 insertions(+), 177 deletions(-) create mode 100644 src/main/java/com/github/thed2lab/analysis/Constants.java diff --git a/src/main/java/com/github/thed2lab/analysis/Analysis.java b/src/main/java/com/github/thed2lab/analysis/Analysis.java index bcc377a..e77cd77 100644 --- a/src/main/java/com/github/thed2lab/analysis/Analysis.java +++ b/src/main/java/com/github/thed2lab/analysis/Analysis.java @@ -1,5 +1,8 @@ package com.github.thed2lab.analysis; +import static com.github.thed2lab.analysis.Constants.SCREEN_HEIGHT; +import static com.github.thed2lab.analysis.Constants.SCREEN_WIDTH; + import java.io.File; import java.util.ArrayList; import java.util.Arrays; @@ -12,8 +15,6 @@ public class Analysis { * Its role is to iterate over each file, and process it into DataEntry objects for gaze, * validity, and fixations. */ - private final static int SCREEN_WIDTH = 1920; - private final static int SCREEN_HEIGHT = 1080; private final static int MIN_PATTERN_LENGTH = 3; private final static int MAX_PATTERN_LENGTH = 7; @@ -54,10 +55,10 @@ public boolean run() { System.out.println("Analyzing " + pName); // Build DataEntrys - DataEntry allGaze = FileHandler.buildDataEntry(f); - DataEntry validGaze = DataFilter.filterByValidity(allGaze); + DataEntry allGaze = DataFilter.applyScreenSize(FileHandler.buildDataEntry(f), SCREEN_WIDTH, SCREEN_HEIGHT); + DataEntry validGaze = DataFilter.filterByValidity(allGaze, SCREEN_WIDTH, SCREEN_HEIGHT); DataEntry fixations = DataFilter.filterByFixations(allGaze); - DataEntry validFixations = DataFilter.filterByValidity(fixations); + DataEntry validFixations = DataFilter.filterByValidity(fixations, SCREEN_HEIGHT, SCREEN_WIDTH); // Write DataEntrys to file validGaze.writeToCSV(pDirectory, pName + "_valid_all_gaze"); @@ -81,7 +82,7 @@ public boolean run() { allParticipantDGMs.add(dgms); // Generate AOIs - AreaOfInterests.generateAOIs(allGaze, pDirectory, pName); + AreaOfInterests.generateAOIs(allGaze, fixations, pDirectory, pName); // Generate windows Windows.generateWindows(allGaze, pDirectory, settings); @@ -149,9 +150,7 @@ public boolean run() { * @return a {@code List} where the first inner-list is the measure names, and second inner-list is the calculated values. */ static List> generateResults(DataEntry allGaze, DataEntry fixations) { - DataEntry validGaze = DataFilter.applyScreenSize(DataFilter.filterByValidity(allGaze), SCREEN_WIDTH, SCREEN_HEIGHT); - DataEntry validAoiFixations = DataFilter.applyScreenSize(DataFilter.filterByValidity(fixations), SCREEN_WIDTH, SCREEN_HEIGHT); - var results = generateResultsHelper(validGaze, validGaze, validAoiFixations); + var results = generateResultsHelper(allGaze, allGaze, fixations); return results; } @@ -164,33 +163,35 @@ static List> generateResults(DataEntry allGaze, DataEntry fixations * @return a {@code List} where the first inner-list is the measure names, and second inner-list is the calculated values. */ static List> generateResults(DataEntry allGaze, DataEntry aoiGaze, DataEntry aoiFixations) { - DataEntry validAllGaze = DataFilter.applyScreenSize(DataFilter.filterByValidity(allGaze), SCREEN_WIDTH, SCREEN_HEIGHT); - DataEntry validAoiGaze = DataFilter.applyScreenSize(DataFilter.filterByValidity(allGaze), SCREEN_WIDTH, SCREEN_HEIGHT); - DataEntry validAoiFixations = DataFilter.applyScreenSize(DataFilter.filterByValidity(aoiFixations), SCREEN_WIDTH, SCREEN_HEIGHT); - var results = generateResultsHelper(validAllGaze, validAoiGaze, validAoiFixations); + var results = generateResultsHelper(allGaze, aoiGaze, aoiFixations); return results; } /** * Helper methods that generates the descriptive gaze measures. - * @param validAllGaze all gaze data, filtered by validity. - * @param validAoiGaze the gaze data that ocurred within an aoi, filtered by validity. For the whole screen, this is the same - * data as validAllGaze. - * @param validAoiFixation the gaze data that ocurred within an aoi, filtered by fixation and validity. - * @return a list the descriptive gaze measures where the first row is the headers and the second row is the values. + * @param allGaze all gaze data for the whole screen, with screen size applied. + * @param areaGaze the gaze data that ocurred within a target portion of screen, i.e., either the whole screen or an AOI, with screen + * size applied. + * @param areaFixations the gaze data that ocurred within a target portion of screen, i.e., either the whole screen or an AOI, + * and filtered by fixation with screen size applied. + * @return a {@code List} where the first inner-list is the measure names, and second inner-list is the calculated values. */ - private static List> generateResultsHelper(DataEntry validAllGaze, DataEntry validAoiGaze, DataEntry validAoiFixation) { + private static List> generateResultsHelper(DataEntry allGaze, DataEntry areaGaze, DataEntry areaFixations) { + // an argument could be made to filter before entering this function; there is a code smell due to blink rate and saccadeV changing + DataEntry validAllGaze = DataFilter.filterByValidity(allGaze, SCREEN_WIDTH, SCREEN_HEIGHT); + DataEntry validAreaGaze = DataFilter.filterByValidity(areaGaze, SCREEN_WIDTH, SCREEN_HEIGHT); + DataEntry validAreaFixations = DataFilter.filterByValidity(areaFixations, SCREEN_WIDTH, SCREEN_HEIGHT); LinkedHashMap resultsMap = new LinkedHashMap(); - resultsMap.putAll(Fixations.analyze(validAoiFixation)); - resultsMap.putAll(Saccades.analyze(validAoiFixation)); - resultsMap.putAll(SaccadeVelocity.analyze(validAllGaze, validAoiFixation)); - resultsMap.putAll(Angles.analyze(validAoiFixation)); - resultsMap.putAll(ConvexHull.analyze(validAoiFixation)); - resultsMap.putAll(GazeEntropy.analyze(validAoiFixation)); - resultsMap.putAll(Blinks.analyze(validAoiGaze)); - resultsMap.putAll(Gaze.analyze(validAoiGaze)); - resultsMap.putAll(Event.analyze(validAoiGaze)); + resultsMap.putAll(Fixations.analyze(validAreaFixations)); + resultsMap.putAll(Saccades.analyze(validAreaFixations)); + resultsMap.putAll(SaccadeVelocity.analyze(validAllGaze, validAreaFixations)); + resultsMap.putAll(Angles.analyze(validAreaFixations)); + resultsMap.putAll(ConvexHull.analyze(validAreaFixations)); + resultsMap.putAll(GazeEntropy.analyze(validAreaFixations)); + resultsMap.putAll(Blinks.analyze(areaGaze)); + resultsMap.putAll(Gaze.analyze(validAreaGaze)); + resultsMap.putAll(Event.analyze(validAreaGaze)); var resultsList = new ArrayList>(2); resultsList.add(new ArrayList<>(resultsMap.keySet())); diff --git a/src/main/java/com/github/thed2lab/analysis/Angles.java b/src/main/java/com/github/thed2lab/analysis/Angles.java index 7de333d..b7c8f70 100644 --- a/src/main/java/com/github/thed2lab/analysis/Angles.java +++ b/src/main/java/com/github/thed2lab/analysis/Angles.java @@ -1,12 +1,13 @@ package com.github.thed2lab.analysis; +import static com.github.thed2lab.analysis.Constants.FIXATION_ID; +import static com.github.thed2lab.analysis.Constants.FIXATION_X; +import static com.github.thed2lab.analysis.Constants.FIXATION_Y; + import java.util.ArrayList; import java.util.LinkedHashMap; public class Angles { - final static String FIXATIONID_INDEX = "FPOGID"; - final static String FIXATIONX_INDEX = "FPOGX"; - final static String FIXATIONY_INDEX = "FPOGY"; static public LinkedHashMap analyze(DataEntry data) { LinkedHashMap results = new LinkedHashMap(); @@ -14,9 +15,9 @@ static public LinkedHashMap analyze(DataEntry data) { for (int row = 0; row < data.rowCount(); row++) { Coordinate eachCoordinate = new Coordinate( - Double.valueOf(data.getValue(FIXATIONX_INDEX, row)), - Double.valueOf(data.getValue(FIXATIONY_INDEX, row)), - Integer.valueOf(data.getValue(FIXATIONID_INDEX, row)) + Double.valueOf(data.getValue(FIXATION_X, row)), + Double.valueOf(data.getValue(FIXATION_Y, row)), + Integer.valueOf(data.getValue(FIXATION_ID, row)) ); allCoordinates.add(eachCoordinate); } diff --git a/src/main/java/com/github/thed2lab/analysis/AreaOfInterests.java b/src/main/java/com/github/thed2lab/analysis/AreaOfInterests.java index 999d81d..593861c 100644 --- a/src/main/java/com/github/thed2lab/analysis/AreaOfInterests.java +++ b/src/main/java/com/github/thed2lab/analysis/AreaOfInterests.java @@ -1,25 +1,27 @@ package com.github.thed2lab.analysis; -import java.util.Arrays; +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; import java.util.LinkedHashMap; import java.util.List; import java.util.Set; public class AreaOfInterests { - final static String FIXATIONID_INDEX = "FPOGID"; //CNT - final static String DURATION_INDEX = "FPOGD"; - final static String AOI_INDEX = "AOI"; - private static final String[] additionalHeaders = {"aoi", "proportion_of_fixations_spent_in_aoi","proportion_of_fixations_durations_spent_in_aoi"}; private static final String[] perAoiHeaders = {"aoi_pair", "transition_count", "proportion_including_self_transitions", "proportion_excluding_self_transitions"}; - public static void generateAOIs(DataEntry allGazeData, String outputDirectory, String fileName) { + public static void generateAOIs(DataEntry allGazeData, DataEntry fixationData, String outputDirectory, String fileName) { LinkedHashMap aoiMetrics = new LinkedHashMap<>(); for (int i = 0; i < allGazeData.rowCount(); i++) { - String aoi = allGazeData.getValue(AOI_INDEX, i); + String aoi = allGazeData.getValue(AOI_LABEL, i); String aoiKey = aoi.equals("") ? "Undefined Area" : aoi; if (!aoiMetrics.containsKey(aoiKey)) { DataEntry d = new DataEntry(allGazeData.getHeaders()); @@ -28,18 +30,17 @@ public static void generateAOIs(DataEntry allGazeData, String outputDirectory, S aoiMetrics.get(aoiKey).process(allGazeData.getRow(i)); } - + DataEntry filteredFixations = DataFilter.filterByValidity(fixationData, SCREEN_WIDTH, SCREEN_HEIGHT); LinkedHashMap aoiFixationMetrics = new LinkedHashMap<>(); - DataEntry allFixations = DataFilter.filterByValidity(DataFilter.filterByFixations(allGazeData)); //System.out.println(allFixations.rowCount()); - for (int i = 0; i < allFixations.rowCount(); i++) { - String aoi = allFixations.getValue(AOI_INDEX, i); + for (int i = 0; i < filteredFixations.rowCount(); i++) { + String aoi = filteredFixations.getValue(AOI_LABEL, i); String aoiKey = aoi.equals("") ? "Undefined Area" : aoi; if (!aoiFixationMetrics.containsKey(aoiKey)) { - DataEntry d = new DataEntry(allFixations.getHeaders()); + DataEntry d = new DataEntry(filteredFixations.getHeaders()); aoiFixationMetrics.put(aoiKey, d); } - aoiFixationMetrics.get(aoiKey).process(allFixations.getRow(i)); + aoiFixationMetrics.get(aoiKey).process(filteredFixations.getRow(i)); } // For any AOIs not in aoiFixationMetrics, add an empty DataEntry @@ -59,7 +60,7 @@ public static void generateAOIs(DataEntry allGazeData, String outputDirectory, S ArrayList> metrics = new ArrayList<>(); metrics.add(new ArrayList()); - double totalDuration = getDuration(allFixations); + double totalDuration = getDuration(filteredFixations); LinkedHashMap validAOIs = new LinkedHashMap<>(); boolean isFirst = true; Set aoiKeySet = aoiMetrics.keySet(); @@ -80,11 +81,11 @@ public static void generateAOIs(DataEntry allGazeData, String outputDirectory, S } results.get(1).add(aoiKey); metrics.add(results.get(1)); - metrics.get(row).addAll(getProportions(allFixations, singleAoiFixations, totalDuration)); + metrics.get(row).addAll(getProportions(filteredFixations, singleAoiFixations, totalDuration)); validAOIs.put(aoiKey, singleAoiFixations); row++; } - ArrayList> pairResults = generatePairResults(allFixations, aoiMetrics); + ArrayList> pairResults = generatePairResults(filteredFixations, aoiMetrics); FileHandler.writeToCSV(metrics, outputDirectory, fileName + "_AOI_DGMs"); FileHandler.writeToCSV(pairResults, outputDirectory, fileName+"_AOI_Transitions"); } @@ -102,7 +103,7 @@ public static ArrayList getProportions(DataEntry fixations, DataEntry ao public static double getDuration(DataEntry fixations) { double durationSum = 0.0; for (int i = 0; i < fixations.rowCount(); i++) { - durationSum += Double.valueOf(fixations.getValue(DURATION_INDEX, i)); + durationSum += Double.valueOf(fixations.getValue(FIXATION_DURATION, i)); } return durationSum; @@ -112,12 +113,12 @@ public static ArrayList> generatePairResults(DataEntry fixations, L LinkedHashMap> totalTransitions = new LinkedHashMap<>(); // ArrayList(Transtions, Inclusive, Exlusive); LinkedHashMap> transitionCounts = new LinkedHashMap<>(); for (int i = 0; i < fixations.rowCount()-1; i++) { - String curAoi = fixations.getValue(AOI_INDEX, i); + String curAoi = fixations.getValue(AOI_LABEL, i); curAoi = curAoi.equals("") ? "Undefined Area" : curAoi; - int curId = Integer.valueOf(fixations.getValue(FIXATIONID_INDEX, i)); - String nextAoi = fixations.getValue(AOI_INDEX, i+1); + int curId = Integer.valueOf(fixations.getValue(FIXATION_ID, i)); + String nextAoi = fixations.getValue(AOI_LABEL, i+1); nextAoi = nextAoi.equals("") ? "Undefined Area" : nextAoi; - int nextId = Integer.valueOf(fixations.getValue(FIXATIONID_INDEX, i+1)); + int nextId = Integer.valueOf(fixations.getValue(FIXATION_ID, i+1)); boolean isValidAOI = (validAOIs.containsKey(curAoi) && validAOIs.containsKey(nextAoi)); if (isValidAOI && nextId == curId + 1) { //Check if fixations are subsequent if (!totalTransitions.containsKey(curAoi)) { //Ensure AOI is initialized in map. diff --git a/src/main/java/com/github/thed2lab/analysis/Blinks.java b/src/main/java/com/github/thed2lab/analysis/Blinks.java index 93e4295..09d16c4 100644 --- a/src/main/java/com/github/thed2lab/analysis/Blinks.java +++ b/src/main/java/com/github/thed2lab/analysis/Blinks.java @@ -1,13 +1,15 @@ package com.github.thed2lab.analysis; +import static com.github.thed2lab.analysis.Constants.BLINK_ID; +import static com.github.thed2lab.analysis.Constants.DATA_ID; +import static com.github.thed2lab.analysis.Constants.TIMESTAMP; + import java.util.LinkedHashMap; public class Blinks { - final static String BLINK_ID_INDEX = "BKID"; - final static String TIME_INDEX = "TIME"; - final static String DATA_ID_INDEX = "CNT"; // unique for each line of raw data - final static String DEFAULT_BKID = "0"; // BKID when no blink is detected + /** Blink id when no blink is being detected. */ + final static String DEFAULT_BKID = "0"; static public LinkedHashMap analyze(DataEntry allGazeData) { @@ -18,14 +20,14 @@ static public LinkedHashMap analyze(DataEntry allGazeData) { double prevTimestamp = 0; int prevDataId = -10; // IDs are always non-negative for (int i = 0; i < allGazeData.rowCount(); i++) { - int curDataId = Integer.parseInt(allGazeData.getValue(DATA_ID_INDEX, i)); - double curTimestamp = Double.parseDouble(allGazeData.getValue(TIME_INDEX, i)); + int curDataId = Integer.parseInt(allGazeData.getValue(DATA_ID, i)); + double curTimestamp = Double.parseDouble(allGazeData.getValue(TIMESTAMP, i)); // calculate time window between data records if they are consecutive if (curDataId == prevDataId + 1) { timeTotal += curTimestamp - prevTimestamp; } - String curBlinkId = allGazeData.getValue(BLINK_ID_INDEX, i); + String curBlinkId = allGazeData.getValue(BLINK_ID, i); if (!curBlinkId.equals(DEFAULT_BKID) && !curBlinkId.equals(prevBlinkId)) { blinkCnt++; // new blink occurred } diff --git a/src/main/java/com/github/thed2lab/analysis/Constants.java b/src/main/java/com/github/thed2lab/analysis/Constants.java new file mode 100644 index 0000000..1609c57 --- /dev/null +++ b/src/main/java/com/github/thed2lab/analysis/Constants.java @@ -0,0 +1,64 @@ +package com.github.thed2lab.analysis; + +/** + * Hardcoded constants that are used throughout the package so there are fewer duplicated + * constants throughout the files. + */ +final class Constants { + /* + * Notice the access modifier is default (package-private). + * We could make this an injectable if we wanted to be better OO programmers, but I think + * this package is closely coupled enough that we don't care anymore. + */ + + private Constants() { + // do not instantiate this class EVER!!! + } + + /** Screen width in pixels */ + final static int SCREEN_WIDTH = 1920; + /** Screen height in pixels */ + final static int SCREEN_HEIGHT = 1080; + + /** Header for timestamp of when the data line was recorded since the start of the recording in seconds */ + final static String TIMESTAMP = "TIME"; + /** Header for the unique ID given to each line of data */ + final static String DATA_ID = "CNT"; + + /** Header for fixation ID. */ + final static String FIXATION_ID = "FPOGID"; + /** Header for the fixations starting timestamp */ + final static String FIXATION_START = "FPOGS"; + /** Header for fixation validity. 1 for true and 2 for false */ + final static String FIXATION_VALIDITY = "FPOGV"; + /** Header for the x-coordinate of the fixation point of gaze */ + final static String FIXATION_X = "FPOGX"; + /** Header for the y-coordinate of the fixation point of gaze */ + final static String FIXATION_Y = "FPOGY"; + /** Header for the duration of a fixation */ + final static String FIXATION_DURATION = "FPOGD"; + + /** Header for the diameter of the left pupil in mm */ + final static String LEFT_PUPIL_DIAMETER = "LPMM"; + /** Header for the valid flag for the left pupil. A value of 1 is valid */ + final static String LEFT_PUPIL_VALIDITY = "LPMMV"; + /** Header for the diameter of the right pupil in mm */ + final static String RIGHT_PUPIL_DIAMETER = "RPMM"; + /** Header for the valid flag for the right pupil. A value of 1 is valid */ + final static String RIGHT_PUPIL_VALIDITY = "RPMMV"; + + /** Header for cursor events */ + final static String CURSOR_EVENT = "CS"; + /** Header for the blink ID */ + final static String BLINK_ID = "BKID"; + /** Header for the blink rate per minute */ + final static String BLINK_RATE = "BKPMIN"; + /** Header for the AOI Label */ + final static String AOI_LABEL = "AOI"; + + /** Header for the saccade magnitude */ + final static String SACCADE_MAGNITUDE = "SACCADE_MAG"; + /** Header for the saccade direction */ + final static String SACCADE_DIR = "SACCADE_DIR"; + +} diff --git a/src/main/java/com/github/thed2lab/analysis/ConvexHull.java b/src/main/java/com/github/thed2lab/analysis/ConvexHull.java index ee55c4b..8dbb912 100644 --- a/src/main/java/com/github/thed2lab/analysis/ConvexHull.java +++ b/src/main/java/com/github/thed2lab/analysis/ConvexHull.java @@ -1,5 +1,8 @@ package com.github.thed2lab.analysis; +import static com.github.thed2lab.analysis.Constants.FIXATION_X; +import static com.github.thed2lab.analysis.Constants.FIXATION_Y; + import java.util.ArrayList; import java.util.Comparator; import java.util.LinkedHashMap; @@ -11,16 +14,14 @@ import java.awt.Point; public class ConvexHull { - final static String FIXATIONX_INDEX = "FPOGX"; - final static String FIXATIONY_INDEX = "FPOGY"; static public LinkedHashMap analyze(DataEntry data) { LinkedHashMap results = new LinkedHashMap(); List allPoints = new ArrayList<>(); for (int row = 0; row < data.rowCount(); row++) { - double x = Double.valueOf(data.getValue(FIXATIONX_INDEX, row)); - double y = Double.valueOf(data.getValue(FIXATIONY_INDEX, row)); + double x = Double.valueOf(data.getValue(FIXATION_X, row)); + double y = Double.valueOf(data.getValue(FIXATION_Y, row)); allPoints.add(new Point2D.Double(x, y)); } List boundingPoints = getConvexHull(allPoints); diff --git a/src/main/java/com/github/thed2lab/analysis/DataFilter.java b/src/main/java/com/github/thed2lab/analysis/DataFilter.java index b421c21..6781204 100644 --- a/src/main/java/com/github/thed2lab/analysis/DataFilter.java +++ b/src/main/java/com/github/thed2lab/analysis/DataFilter.java @@ -1,7 +1,15 @@ package com.github.thed2lab.analysis; +import static com.github.thed2lab.analysis.Constants.FIXATION_ID; +import static com.github.thed2lab.analysis.Constants.FIXATION_VALIDITY; +import static com.github.thed2lab.analysis.Constants.FIXATION_X; +import static com.github.thed2lab.analysis.Constants.FIXATION_Y; +import static com.github.thed2lab.analysis.Constants.LEFT_PUPIL_DIAMETER; +import static com.github.thed2lab.analysis.Constants.LEFT_PUPIL_VALIDITY; +import static com.github.thed2lab.analysis.Constants.RIGHT_PUPIL_DIAMETER; +import static com.github.thed2lab.analysis.Constants.RIGHT_PUPIL_VALIDITY; + import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.List; // Note: Order used for data filtering matters and can invalidate DataEntrys if used incorrectly @@ -14,8 +22,8 @@ static public DataEntry filterByFixations(DataEntry data) { int currFixation = 1; for (int row = 0; row < data.rowCount(); row++) { - int fixationID = Integer.parseInt(data.getValue("FPOGID", row)); - int fixationValidity = Integer.parseInt(data.getValue("FPOGV", row)); + int fixationID = Integer.parseInt(data.getValue(FIXATION_ID, row)); + int fixationValidity = Integer.parseInt(data.getValue(FIXATION_VALIDITY, row)); if (fixationID != currFixation) { if (lastValidFixation != null) filtered.process(lastValidFixation); if (fixationValidity == 1) lastValidFixation = data.getRow(row); // Edge case; check to see if the first line associated with a given fixation is valid @@ -35,37 +43,51 @@ static public DataEntry filterByFixations(DataEntry data) { /** * Cleanses data by filtering out invalid data. Valid data entries must occur within * the bounds of the monitor and have humanly possible pupil dilation. - * @param data Data to be cleansed + * @param data data to be cleansed. */ public static DataEntry filterByValidity(DataEntry data) { + // GazePoint scales point of gaze location from 0 to 1 when on screen + final int MAX_SCREEN_WIDTH = 1; + final int MAX_SCREEN_HEIGHT = 1; + return filterByValidity(data, MAX_SCREEN_WIDTH, MAX_SCREEN_HEIGHT); + } + + /** + * Cleanses data by filtering out invalid data. Valid data entries must occur within + * the bounds of the monitor and have humanly possible pupil dilation. + * @param data data to be cleansed. + * @param screenWidth screen width scaler that was previously applied to the data. + * @param screenHeight screen height scaler that was previously applied to the data. + */ + public static DataEntry filterByValidity(DataEntry data, int screenWidth, int screenHeight) { // humanly possible pupil diameter is between 2 and 8 mm final int MIN_DIAMETER = 2; final int MAX_DIAMETER = 8; final int MAX_PUPIL_DIFF = 1; - // GazePoint scales point of gaze location from 0 to 1 when on screen - final int MIN_SCREEN_DIM = 0; - final int MAX_SCREEN_DIM = 1; + // GazePoint scales point of gaze location from 0 to 1 when on screen. The data + // may have been scaled so we only hardcode the 0 + final int MIN_SCREEN_DIM = 0; DataEntry filtered = new DataEntry(data.getHeaders()); for (int rowNum = 0; rowNum < data.rowCount(); rowNum++) { // Note: It is extremely slow to parse a string over and over again // Check if Gazepoint could detect the pupils - boolean leftValid = Integer.parseInt(data.getValue("LPMMV", rowNum)) == 1; - boolean rightValid = Integer.parseInt(data.getValue("RPMMV", rowNum)) == 1; + boolean leftValid = Integer.parseInt(data.getValue(LEFT_PUPIL_VALIDITY, rowNum)) == 1; + boolean rightValid = Integer.parseInt(data.getValue(RIGHT_PUPIL_VALIDITY, rowNum)) == 1; if (!(leftValid && rightValid)) { continue; // skip invalid entry } // Check if POG is on the screen - float xCoordinate = Float.parseFloat(data.getValue("FPOGX" ,rowNum)); - float yCoordinate = Float.parseFloat(data.getValue("FPOGY" ,rowNum)); - if (xCoordinate < MIN_SCREEN_DIM || xCoordinate > MAX_SCREEN_DIM) { + float xCoordinate = Float.parseFloat(data.getValue(FIXATION_X ,rowNum)); + float yCoordinate = Float.parseFloat(data.getValue(FIXATION_Y ,rowNum)); + if (xCoordinate < MIN_SCREEN_DIM || xCoordinate > screenWidth) { continue; // off screen in x-direction, invalid - } else if (yCoordinate < MIN_SCREEN_DIM || yCoordinate > MAX_SCREEN_DIM) { + } else if (yCoordinate < MIN_SCREEN_DIM || yCoordinate > screenHeight) { continue; // off screen in y-direction, invalid entry } // Check if pupils are valid sizes individually and compared to each other. - float leftDiameter = Float.parseFloat(data.getValue("LPMM", rowNum)); - float rightDiameter = Float.parseFloat(data.getValue("RPMM", rowNum)); + float leftDiameter = Float.parseFloat(data.getValue(LEFT_PUPIL_DIAMETER, rowNum)); + float rightDiameter = Float.parseFloat(data.getValue(RIGHT_PUPIL_DIAMETER, rowNum)); if ( leftDiameter >= MIN_DIAMETER && leftDiameter <= MAX_DIAMETER @@ -87,8 +109,8 @@ static public DataEntry applyScreenSize(DataEntry data, int screenWidth, int scr List newRow = new ArrayList(); newRow.addAll(currentRow); - int fixationXIndex = data.getHeaderIndex("FPOGX"); - int fixationYIndex = data.getHeaderIndex("FPOGY"); + int fixationXIndex = data.getHeaderIndex(FIXATION_X); + int fixationYIndex = data.getHeaderIndex(FIXATION_Y); newRow.set(fixationXIndex,String.valueOf(Double.valueOf(newRow.get(fixationXIndex)) * screenWidth)); newRow.set(fixationYIndex,String.valueOf(Double.valueOf(newRow.get(fixationYIndex)) * screenHeight)); @@ -98,10 +120,4 @@ static public DataEntry applyScreenSize(DataEntry data, int screenWidth, int scr return filtered; } - - - static public LinkedHashMap filterByAOI(DataEntry data ){ - - return new LinkedHashMap(); - } } diff --git a/src/main/java/com/github/thed2lab/analysis/Event.java b/src/main/java/com/github/thed2lab/analysis/Event.java index b361401..d3b14f8 100644 --- a/src/main/java/com/github/thed2lab/analysis/Event.java +++ b/src/main/java/com/github/thed2lab/analysis/Event.java @@ -1,16 +1,17 @@ package com.github.thed2lab.analysis; +import static com.github.thed2lab.analysis.Constants.CURSOR_EVENT; + import java.util.LinkedHashMap; public class Event { - final static String INPUT_INDEX = "CS"; static public LinkedHashMap analyze(DataEntry data) { LinkedHashMap results = new LinkedHashMap(); int leftMouseClicks = 0; for (int row = 0; row < data.rowCount(); row++) { - if (data.getValue(INPUT_INDEX, row).equals("1")) { + if (data.getValue(CURSOR_EVENT, row).equals("1")) { leftMouseClicks += 1; } } diff --git a/src/main/java/com/github/thed2lab/analysis/Fixations.java b/src/main/java/com/github/thed2lab/analysis/Fixations.java index e8cf6c9..55fa52b 100644 --- a/src/main/java/com/github/thed2lab/analysis/Fixations.java +++ b/src/main/java/com/github/thed2lab/analysis/Fixations.java @@ -1,10 +1,11 @@ package com.github.thed2lab.analysis; +import static com.github.thed2lab.analysis.Constants.FIXATION_DURATION; + import java.util.ArrayList; import java.util.LinkedHashMap; public class Fixations { - final static String DURATION_INDEX = "FPOGD"; static public LinkedHashMap analyze(DataEntry data) { LinkedHashMap results = new LinkedHashMap(); @@ -13,7 +14,7 @@ static public LinkedHashMap analyze(DataEntry data) { int fixationCount = data.rowCount(); for (int row = 0; row < data.rowCount(); row++) { - Double fixationDurationSeconds = Double.valueOf(data.getValue(DURATION_INDEX, row)); + Double fixationDurationSeconds = Double.valueOf(data.getValue(FIXATION_DURATION, row)); allFixationDurations.add(fixationDurationSeconds); } diff --git a/src/main/java/com/github/thed2lab/analysis/Gaze.java b/src/main/java/com/github/thed2lab/analysis/Gaze.java index e7e1c6a..1cad24d 100644 --- a/src/main/java/com/github/thed2lab/analysis/Gaze.java +++ b/src/main/java/com/github/thed2lab/analysis/Gaze.java @@ -1,10 +1,10 @@ package com.github.thed2lab.analysis; +import static com.github.thed2lab.analysis.Constants.*; + import java.util.LinkedHashMap; public class Gaze { - final static String PUPIL_LEFT_DIAMETER_INDEX = "LPMM"; - final static String PUPIL_RIGHT_DIAMETER_INDEX = "RPMM"; static public LinkedHashMap analyze(DataEntry data) { LinkedHashMap results = new LinkedHashMap(); @@ -14,8 +14,8 @@ static public LinkedHashMap analyze(DataEntry data) { int count = data.rowCount(); for (int row = 0; row < data.rowCount(); row++) { - double leftSize = Double.valueOf(data.getValue(PUPIL_LEFT_DIAMETER_INDEX, row)); - double rightSize = Double.valueOf(data.getValue(PUPIL_RIGHT_DIAMETER_INDEX, row)); + double leftSize = Double.valueOf(data.getValue(LEFT_PUPIL_DIAMETER, row)); + double rightSize = Double.valueOf(data.getValue(RIGHT_PUPIL_DIAMETER, row)); leftSum += leftSize; rightSum += rightSize; bothSum += (leftSize + rightSize) / 2.0; diff --git a/src/main/java/com/github/thed2lab/analysis/GazeEntropy.java b/src/main/java/com/github/thed2lab/analysis/GazeEntropy.java index 1e1f4ad..bc90ee1 100644 --- a/src/main/java/com/github/thed2lab/analysis/GazeEntropy.java +++ b/src/main/java/com/github/thed2lab/analysis/GazeEntropy.java @@ -1,12 +1,13 @@ package com.github.thed2lab.analysis; +import static com.github.thed2lab.analysis.Constants.AOI_LABEL; + import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; public class GazeEntropy { - private final static String AOI_INDEX = "AOI"; /** * Calculates the stationary entropy and transition entropy measures. The method iterates over the participant’s @@ -25,7 +26,7 @@ static public LinkedHashMap analyze(DataEntry fixations) { int fixationCount = fixations.rowCount(); for (int row = 0; row < fixations.rowCount(); row++) { - String aoi = fixations.getValue(AOI_INDEX, row); + String aoi = fixations.getValue(AOI_LABEL, row); aoiSequence.add(aoi); aoiProbability.put(aoi, aoiProbability.getOrDefault(aoi, 0.0) + 1); if (lastAoi != null) { // skips the first loop diff --git a/src/main/java/com/github/thed2lab/analysis/Parameters.java b/src/main/java/com/github/thed2lab/analysis/Parameters.java index 3cb11e4..0a6c6e7 100644 --- a/src/main/java/com/github/thed2lab/analysis/Parameters.java +++ b/src/main/java/com/github/thed2lab/analysis/Parameters.java @@ -47,19 +47,6 @@ public String toString() { return "--Parameters-- \n InputFiles: ["+inputFiles.length+"] "+Arrays.toString(inputFiles)+" \n OutputDirectory: "+outputDirectory +"\n --End of Parameters--"; } - /* public static void main(String[] args) { - System.out.println("Creating and saving Parameters!"); - - // Parameters p = new Parameters(new String[]{"data\\Kayla_all_gaze.csv","data\\Esther Jung_all_gaze.csv"},"data\\presets", new HashMap<>()); - // p.saveToJSON("data\\presets","TestConfig.json"); - - // System.out.println(p.toString()); - // System.out.println("Loading parameters!"); - - Parameters p2 = new Parameters(new File("data\\presets\\TestConfig.json")); - System.out.println(p2.toString()); - } */ - public File[] getInputFiles() { return this.inputFiles.clone(); } diff --git a/src/main/java/com/github/thed2lab/analysis/Patterns.java b/src/main/java/com/github/thed2lab/analysis/Patterns.java index e27813f..b4d6586 100644 --- a/src/main/java/com/github/thed2lab/analysis/Patterns.java +++ b/src/main/java/com/github/thed2lab/analysis/Patterns.java @@ -34,12 +34,6 @@ public static ArrayList> discoverPatterns(List sequences, i double averagePatternFrequency = (double) frequencyMap.get(pattern)/sequences.size(); double proportionalPatternFrequency = (double) frequencyMap.get(pattern)/totalPatternCount; - // System.out.print(pattern + " "); - // System.out.print(frequency + " "); - // System.out.print(sequenceSupport + " "); - // System.out.print(averagePatternFrequency + " "); - // System.out.println(proportionalPatternFrequency + " "); - if (frequency >= minFrequency && sequenceMap.get(pattern).size() >= minSequenceSize) { List patternData = new ArrayList(); patternData.add(pattern); diff --git a/src/main/java/com/github/thed2lab/analysis/SaccadeVelocity.java b/src/main/java/com/github/thed2lab/analysis/SaccadeVelocity.java index 8501f45..4be9668 100644 --- a/src/main/java/com/github/thed2lab/analysis/SaccadeVelocity.java +++ b/src/main/java/com/github/thed2lab/analysis/SaccadeVelocity.java @@ -1,24 +1,24 @@ package com.github.thed2lab.analysis; +import static com.github.thed2lab.analysis.Constants.FIXATION_ID; +import static com.github.thed2lab.analysis.Constants.FIXATION_VALIDITY; +import static com.github.thed2lab.analysis.Constants.FIXATION_X; +import static com.github.thed2lab.analysis.Constants.FIXATION_Y; +import static com.github.thed2lab.analysis.Constants.TIMESTAMP; + import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; public class SaccadeVelocity { - final static String TIME_INDEX = "TIME"; - final static String FIXATIONID_INDEX = "FPOGID"; - final static String FIXATIONX_INDEX = "FPOGX"; - final static String FIXATIONY_INDEX = "FPOGY"; - final static String FIXATION_VALIDITY_INDEX = "FPOGV"; - final static String FIXATION_DURATION_INDEX = "FPOGD"; /** * Calculates the average peak saccade velocity. Iterates over all rows of a participant’s gaze. * If the current row’s fixation ID (“FID”) and the next consecutive fixation ID both appear in * the fixation data as well as if the fixation validity (“FPOGV”) is set to 0, then the row is * considered part of a saccade. - * @param allGazeData all gaze data, filtered by validity with screen size applied. When calculating - * for AOIs, use all gaze data, not just the AOI specific gaze data. + * @param allGazeData all gaze data, with screen size applied. When calculating for AOIs, use all + * gaze data, not just the AOI specific gaze data. * @param fixationData the gaze data, filtered by fixation and validity with screen size applied. * When calculating for AOIs, use only fixation data that occurs within the AOI. * @return average peak saccade velocity’s header mapped to the calculated value as a {@code String}. @@ -36,8 +36,8 @@ static public LinkedHashMap analyze(DataEntry allGazeData, DataEn while ((fixDataIndex < fixationData.rowCount() - 1) && (gazeDataIndex < allGazeData.rowCount())) { // Get the fixation Id of the next saccade that occurs completely within portion of screen (whole or AOI) while (fixDataIndex < fixationData.rowCount() - 1) { - int curFixId = Integer.parseInt(fixationData.getValue(FIXATIONID_INDEX, fixDataIndex)); - int nextFixId = Integer.parseInt(fixationData.getValue(FIXATIONID_INDEX, fixDataIndex + 1)); + int curFixId = Integer.parseInt(fixationData.getValue(FIXATION_ID, fixDataIndex)); + int nextFixId = Integer.parseInt(fixationData.getValue(FIXATION_ID, fixDataIndex + 1)); fixDataIndex++; if (nextFixId == curFixId + 1) { targetFixId = curFixId; @@ -46,7 +46,7 @@ static public LinkedHashMap analyze(DataEntry allGazeData, DataEn } while (gazeDataIndex < allGazeData.rowCount()) { - int curId = Integer.parseInt(allGazeData.getValue(FIXATIONID_INDEX, gazeDataIndex)); + int curId = Integer.parseInt(allGazeData.getValue(FIXATION_ID, gazeDataIndex)); if (curId < targetFixId) { gazeDataIndex++; continue; @@ -54,16 +54,16 @@ static public LinkedHashMap analyze(DataEntry allGazeData, DataEn break; // could not find target, look for next fixation } - boolean saccade = Integer.parseInt(allGazeData.getValue(FIXATION_VALIDITY_INDEX, gazeDataIndex)) == 0 ? true : false; + boolean saccade = Integer.parseInt(allGazeData.getValue(FIXATION_VALIDITY, gazeDataIndex)) == 0 ? true : false; // Check if not a saccade if (!saccade) { gazeDataIndex++; continue; // go to next data point } - Double x = Double.parseDouble(allGazeData.getValue(FIXATIONX_INDEX, gazeDataIndex)); - Double y = Double.parseDouble(allGazeData.getValue(FIXATIONY_INDEX, gazeDataIndex)); - Double t = Double.parseDouble(allGazeData.getValue(TIME_INDEX, gazeDataIndex)); + Double x = Double.parseDouble(allGazeData.getValue(FIXATION_X, gazeDataIndex)); + Double y = Double.parseDouble(allGazeData.getValue(FIXATION_Y, gazeDataIndex)); + Double t = Double.parseDouble(allGazeData.getValue(TIMESTAMP, gazeDataIndex)); positionProfile.add(new Double[] {x, y, t}); gazeDataIndex++; } diff --git a/src/main/java/com/github/thed2lab/analysis/Saccades.java b/src/main/java/com/github/thed2lab/analysis/Saccades.java index bb84287..a479ad1 100644 --- a/src/main/java/com/github/thed2lab/analysis/Saccades.java +++ b/src/main/java/com/github/thed2lab/analysis/Saccades.java @@ -1,14 +1,15 @@ package com.github.thed2lab.analysis; +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.FIXATION_X; +import static com.github.thed2lab.analysis.Constants.FIXATION_Y; +import static com.github.thed2lab.analysis.Constants.FIXATION_START; + import java.util.ArrayList; import java.util.LinkedHashMap; public class Saccades { - final static String DURATION_INDEX = "FPOGD"; - final static String TIMESTAMP_INDEX = "FPOGS"; - final static String FIXATIONID_INDEX = "FPOGID"; - final static String FIXATIONX_INDEX = "FPOGX"; - final static String FIXATIONY_INDEX = "FPOGY"; static public LinkedHashMap analyze(DataEntry data) { LinkedHashMap results = new LinkedHashMap(); @@ -18,18 +19,18 @@ static public LinkedHashMap analyze(DataEntry data) { ArrayList allCoordinates = new ArrayList<>(); for (int row = 0; row < data.rowCount(); row++) { - Double fixationDurationSeconds = Double.valueOf(data.getValue(DURATION_INDEX, row));; + Double fixationDurationSeconds = Double.valueOf(data.getValue(FIXATION_DURATION, row));; Double[] eachSaccadeDetail = new Double[3]; - eachSaccadeDetail[0] = Double.valueOf(data.getValue(TIMESTAMP_INDEX, row)); - eachSaccadeDetail[1] = Double.valueOf(data.getValue(DURATION_INDEX, row)); - eachSaccadeDetail[2] = Double.valueOf(data.getValue(FIXATIONID_INDEX, row)); + eachSaccadeDetail[0] = Double.valueOf(data.getValue(FIXATION_START, row)); + eachSaccadeDetail[1] = Double.valueOf(data.getValue(FIXATION_DURATION, row)); + eachSaccadeDetail[2] = Double.valueOf(data.getValue(FIXATION_ID, row)); saccadeDetails.add(eachSaccadeDetail); Coordinate eachCoordinate = new Coordinate( - Double.valueOf(data.getValue(FIXATIONX_INDEX, row)), - Double.valueOf(data.getValue(FIXATIONY_INDEX, row)), - Integer.valueOf(data.getValue(FIXATIONID_INDEX, row)) + Double.valueOf(data.getValue(FIXATION_X, row)), + Double.valueOf(data.getValue(FIXATION_Y, row)), + Integer.valueOf(data.getValue(FIXATION_ID, row)) ); allCoordinates.add(eachCoordinate); allFixationDurations.add(fixationDurationSeconds); diff --git a/src/main/java/com/github/thed2lab/analysis/Sequences.java b/src/main/java/com/github/thed2lab/analysis/Sequences.java index cc619a6..f1cacc9 100644 --- a/src/main/java/com/github/thed2lab/analysis/Sequences.java +++ b/src/main/java/com/github/thed2lab/analysis/Sequences.java @@ -1,11 +1,11 @@ package com.github.thed2lab.analysis; +import static com.github.thed2lab.analysis.Constants.AOI_LABEL; + import java.util.HashMap; import java.util.List; public class Sequences { - - final static String AOI_INDEX = "AOI"; public static void generateSequenceFiles(DataEntry data, String outputDirectory, List sequences, HashMap map) { String aoiDescriptions = ""; @@ -21,7 +21,7 @@ public static void generateSequenceFiles(DataEntry data, String outputDirectory, // Generate sequence for (int i = 0; i < data.rowCount(); i++) { - String aoi = data.getValue(AOI_INDEX, i); + String aoi = data.getValue(AOI_LABEL, i); if (!map.containsKey(aoi)) { map.put(aoi, map.size() + ascii); diff --git a/src/main/java/com/github/thed2lab/analysis/Windows.java b/src/main/java/com/github/thed2lab/analysis/Windows.java index 4160cba..1f1b933 100644 --- a/src/main/java/com/github/thed2lab/analysis/Windows.java +++ b/src/main/java/com/github/thed2lab/analysis/Windows.java @@ -1,5 +1,13 @@ package com.github.thed2lab.analysis; +import static com.github.thed2lab.analysis.Constants.TIMESTAMP; +import static com.github.thed2lab.analysis.Constants.BLINK_RATE; +import static com.github.thed2lab.analysis.Constants.FIXATION_DURATION; +import static com.github.thed2lab.analysis.Constants.LEFT_PUPIL_DIAMETER; +import static com.github.thed2lab.analysis.Constants.RIGHT_PUPIL_DIAMETER; +import static com.github.thed2lab.analysis.Constants.SACCADE_DIR; +import static com.github.thed2lab.analysis.Constants.SACCADE_MAGNITUDE; + import java.io.File; import java.util.ArrayList; import java.util.Arrays; @@ -9,29 +17,29 @@ public class Windows { - private final static String TIME_INDEX = "TIME"; private final static int BASELINE_LENGTH = 120; + private final static String LEFT_RIGHT_DIAMETER = LEFT_PUPIL_DIAMETER + " + " + RIGHT_PUPIL_DIAMETER; // Set of supported events that utilize the fixation file final static Set fixationEvents = new HashSet( Arrays.asList( - "FPOGD", - "SACCADE_MAG", - "SACCADE_DIR" + FIXATION_DURATION, + SACCADE_MAGNITUDE, + SACCADE_DIR )); // Set of supported events that utilize the allGaze file final static Set allGazeEvents = new HashSet( Arrays.asList( - "LPMM", - "RPMM", - "BKPMIN", - "LPMM + RPMM" + LEFT_PUPIL_DIAMETER, + RIGHT_PUPIL_DIAMETER, + BLINK_RATE, + LEFT_RIGHT_DIAMETER )); public static void generateWindows(DataEntry allGaze, String outputDirectory, WindowSettings settings) { List headers = allGaze.getHeaders(); - double t0 = Double.valueOf(allGaze.getValue(TIME_INDEX, 0)); + double t0 = Double.valueOf(allGaze.getValue(TIMESTAMP, 0)); // Generate baseline file generateBaselineFile(allGaze, outputDirectory + "/baseline"); @@ -46,7 +54,7 @@ public static void generateWindows(DataEntry allGaze, String outputDirectory, Wi for (int i = 0; i < allGaze.rowCount(); i++) { List currRow = allGaze.getRow(i); - Double t = Double.valueOf(allGaze.getValue(TIME_INDEX, i)); + Double t = Double.valueOf(allGaze.getValue(TIMESTAMP, i)); if (t > end) { end += windowSize; @@ -74,7 +82,7 @@ public static void generateWindows(DataEntry allGaze, String outputDirectory, Wi for (int i = 0; i < allGaze.rowCount(); i++) { List currRow = allGaze.getRow(i); - Double t = Double.valueOf(allGaze.getValue(TIME_INDEX, i)); + Double t = Double.valueOf(allGaze.getValue(TIMESTAMP, i)); if (t > end) { end += windowSize; @@ -102,12 +110,12 @@ public static void generateWindows(DataEntry allGaze, String outputDirectory, Wi double end = start + windowSize; for (int i = 0; i < allGaze.rowCount(); i++) { - double t1 = Double.parseDouble(allGaze.getValue(TIME_INDEX, i)); + double t1 = Double.parseDouble(allGaze.getValue(TIMESTAMP, i)); if (t1 >= start) { for (int j = i; j < allGaze.rowCount(); j++) { List row2 = allGaze.getRow(j); - double t2 = Double.parseDouble(allGaze.getValue(TIME_INDEX, j)); + double t2 = Double.parseDouble(allGaze.getValue(TIMESTAMP, j)); if (t2 >= end || j == allGaze.rowCount() - 1) { window.process(row2); @@ -142,7 +150,7 @@ public static void generateWindows(DataEntry allGaze, String outputDirectory, Wi double baselineValue = getEventBaselineValue(outputDirectory, event); for (int i = 0; i < allGaze.rowCount(); i++) { - Double t = Double.valueOf(allGaze.getValue(TIME_INDEX, i)); + Double t = Double.valueOf(allGaze.getValue(TIMESTAMP, i)); Double windowValue = getEventWindowValue(allGaze, event, i); // Get the initial timestamp @@ -174,18 +182,19 @@ public static void generateWindows(DataEntry allGaze, String outputDirectory, Wi static void outputWindowFiles(ArrayList windows, double t0, String outputDirectory) { int windowCount = 1; List> allWindowDGMs = new ArrayList>(); - for (DataEntry w : windows) { + for (DataEntry windowGaze : windows) { String fileName = "window" + windowCount; String windowDirectory = outputDirectory + "/" + fileName; - w.writeToCSV(windowDirectory, fileName); + windowGaze.writeToCSV(windowDirectory, fileName); // windows are continuous and raw, therefore fixation filtering will be valid - List> results = Analysis.generateResults(w, DataFilter.filterByFixations(w)); + DataEntry windowFixations = DataFilter.filterByFixations(windowGaze); + List> results = Analysis.generateResults(windowGaze, windowFixations); // Calculate beginning time stamp, ending timestamp, window duration, initial/final seconds elapsed since window start - double t1 = Double.parseDouble(w.getValue(TIME_INDEX, 0)); - double t2 = Double.parseDouble(w.getValue(TIME_INDEX, w.rowCount() - 1)); + double t1 = Double.parseDouble(windowGaze.getValue(TIMESTAMP, 0)); + double t2 = Double.parseDouble(windowGaze.getValue(TIMESTAMP, windowGaze.rowCount() - 1)); double windowDuration = t2 - t1; double initialDuration = t1 - t0; double finalDuration = t2 - t0; @@ -211,8 +220,7 @@ static void outputWindowFiles(ArrayList windows, double t0, String ou } FileHandler.writeToCSV(results, windowDirectory, fileName + "_DGMs"); - AreaOfInterests.generateAOIs(w, windowDirectory, fileName); - + AreaOfInterests.generateAOIs(windowGaze, windowFixations, windowDirectory, fileName); windowCount++; } @@ -221,11 +229,11 @@ static void outputWindowFiles(ArrayList windows, double t0, String ou static void generateBaselineFile(DataEntry allGaze, String outputDirectory) { DataEntry baseline = new DataEntry(allGaze.getHeaders()); - double startTime = Double.valueOf(allGaze.getValue(TIME_INDEX, 0)); + double startTime = Double.valueOf(allGaze.getValue(TIMESTAMP, 0)); double endTime = startTime + BASELINE_LENGTH; for (int i = 0; i < allGaze.rowCount(); i++) { - Double t = Double.parseDouble(allGaze.getValue(TIME_INDEX, i)); + Double t = Double.parseDouble(allGaze.getValue(TIMESTAMP, i)); if (t >= endTime) { break; @@ -264,8 +272,8 @@ static double getAveragePupilDilationBaseline(String fileDirectory) { baseline = DataFilter.filterByValidity(baseline); // Filter by validity for (int i = 0; i < baseline.rowCount(); i++) { - double left = Double.parseDouble(baseline.getValue("LPMM", i)); - double right = Double.parseDouble(baseline.getValue("RPMM", i)); + double left = Double.parseDouble(baseline.getValue(LEFT_PUPIL_DIAMETER, i)); + double right = Double.parseDouble(baseline.getValue(RIGHT_PUPIL_DIAMETER, i)); eventValue += ((left + right) / 2); } @@ -276,7 +284,7 @@ static double getAveragePupilDilationBaseline(String fileDirectory) { static double getEventBaselineValue(String fileDirectory, String event) { switch(event) { - case "LPMM + RPMM": + case LEFT_RIGHT_DIAMETER: return getAveragePupilDilationBaseline(fileDirectory); default: return getRawEventBaselineValue(fileDirectory, event); @@ -285,9 +293,9 @@ static double getEventBaselineValue(String fileDirectory, String event) { static double getEventWindowValue(DataEntry d, String event, int row) { switch (event) { - case "LPMM + RPMM": - double left = Double.parseDouble(d.getValue("LPMM", row)); - double right = Double.parseDouble(d.getValue("RPMM", row)); + case LEFT_RIGHT_DIAMETER: + double left = Double.parseDouble(d.getValue(LEFT_PUPIL_DIAMETER, row)); + double right = Double.parseDouble(d.getValue(RIGHT_PUPIL_DIAMETER, row)); return (left + right) / 2; default: return Double.parseDouble(d.getValue(event, row)); From ba238e89defab5869e20999f15856e18daee56a1 Mon Sep 17 00:00:00 2001 From: ashkjones Date: Wed, 7 Aug 2024 16:07:55 -0700 Subject: [PATCH 5/5] Update yml file to use newer version of setup-java --- .github/workflows/maven.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 282279e..37cbfab 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -22,7 +22,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up JDK 21 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: '21' distribution: 'temurin'