Commit cba75c4e authored by roset.alwzrah's avatar roset.alwzrah

Student Analytics

parent 8bca5127
Pipeline #180 failed with stages
...@@ -4,9 +4,10 @@ ...@@ -4,9 +4,10 @@
<option name="autoReloadType" value="SELECTIVE" /> <option name="autoReloadType" value="SELECTIVE" />
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="bd82bf16-4018-4075-b7d4-6d792f3cd58b" name="Changes" comment="Find the number of occurrences of a number in an array"> <list default="true" id="bd82bf16-4018-4075-b7d4-6d792f3cd58b" name="Changes" comment="calculate fibonacci number">
<change afterPath="$PROJECT_DIR$/src/main/java/Service/Fibonacci.java" afterDir="false" /> <change afterPath="$PROJECT_DIR$/src/main/java/Resource/Student.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/test/java/FibonacciTest.java" afterDir="false" /> <change afterPath="$PROJECT_DIR$/src/main/java/Service/StudentAnalytics.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/test/java/StudentAnalyticsTest.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
...@@ -56,7 +57,14 @@ ...@@ -56,7 +57,14 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1679420949412</updated> <updated>1679420949412</updated>
</task> </task>
<option name="localTasksCounter" value="2" /> <task id="LOCAL-00002" summary="calculate fibonacci number">
<created>1679421396058</created>
<option name="number" value="00002" />
<option name="presentableId" value="LOCAL-00002" />
<option name="project" value="LOCAL" />
<updated>1679421396058</updated>
</task>
<option name="localTasksCounter" value="3" />
<servers /> <servers />
</component> </component>
<component name="Vcs.Log.Tabs.Properties"> <component name="Vcs.Log.Tabs.Properties">
...@@ -72,6 +80,7 @@ ...@@ -72,6 +80,7 @@
</component> </component>
<component name="VcsManagerConfiguration"> <component name="VcsManagerConfiguration">
<MESSAGE value="Find the number of occurrences of a number in an array" /> <MESSAGE value="Find the number of occurrences of a number in an array" />
<option name="LAST_COMMIT_MESSAGE" value="Find the number of occurrences of a number in an array" /> <MESSAGE value="calculate fibonacci number" />
<option name="LAST_COMMIT_MESSAGE" value="calculate fibonacci number" />
</component> </component>
</project> </project>
\ No newline at end of file
package Resource;
/**
* A class representing a single student in a single class.
*/
public final class Student {
/**
* First name of the student.
*/
private final String firstName;
/**
* Surname of the student.
*/
private final String lastName;
/**
* Age of the student.
*/
private final double age;
/**
* Grade the student has received in the class so far.
*/
private final int grade;
/**
* Whether the student is currently enrolled, or has already completed the
* course.
*/
private final boolean isCurrent;
/**
* Constructor.
* @param setFirstName Student first name
* @param setLastName Student last name
* @param setAge Student age
* @param setGrade Student grade in course
* @param setIsCurrent Student currently enrolled?
*/
public Student(final String setFirstName, final String setLastName,
final double setAge, final int setGrade,
final boolean setIsCurrent) {
this.firstName = setFirstName;
this.lastName = setLastName;
this.age = setAge;
this.grade = setGrade;
this.isCurrent = setIsCurrent;
}
/**
* Get the first name of this student.
* @return The student's first name.
*/
public String getFirstName() {
return firstName;
}
/**
* Get the last name of this student.
* @return The student's last name.
*/
public String getLastName() {
return lastName;
}
/**
* Get the age of this student.
* @return The student's age.
*/
public double getAge() {
return age;
}
/**
* Get the grade this student has achieved in this course so far.
* @return The student's current grade.
*/
public int getGrade() {
return grade;
}
/**
* Check if this student is active, or has taken the course in the past.
* @return true if the student is currently enrolled, false otherwise
*/
public boolean checkIsCurrent() {
return isCurrent;
}
}
package Service;
import Resource.Student;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* A simple wrapper class for various analytics methods.
*/
public final class StudentAnalytics {
/**
* Sequentially computes the average age of all actively enrolled students
* using loops.
*
* @param studentArray Student data for the class.
* @return Average age of enrolled students
*/
public double averageAgeOfEnrolledStudentsImperative(
final Student[] studentArray) {
List<Student> activeStudents = new ArrayList<Student>();
for (Student s : studentArray) {
if (s.checkIsCurrent()) {
activeStudents.add(s);
}
}
double ageSum = 0.0;
for (Student s : activeStudents) {
ageSum += s.getAge();
}
return ageSum / (double) activeStudents.size();
}
/**
* TODO compute the average age of all actively enrolled students using
* parallel streams. This should mirror the functionality of
* averageAgeOfEnrolledStudentsImperative. This method should not use any
* loops.
*
* @param studentArray Student data for the class.
* @return Average age of enrolled students
*/
public double averageAgeOfEnrolledStudentsParallelStream(
final Student[] studentArray) {
double avg=Stream.of(studentArray).parallel().filter(s -> s.checkIsCurrent()).mapToDouble(s -> s.getAge()).average().getAsDouble();
return avg;
}
/**
* Sequentially computes the most common first name out of all students that
* are no longer active in the class using loops.
*
* @param studentArray Student data for the class.
* @return Most common first name of inactive students
*/
public String mostCommonFirstNameOfInactiveStudentsImperative(
final Student[] studentArray) {
List<Student> inactiveStudents = new ArrayList<Student>();
for (Student s : studentArray) {
if (!s.checkIsCurrent()) {
inactiveStudents.add(s);
}
}
Map<String, Integer> nameCounts = new HashMap<String, Integer>();
for (Student s : inactiveStudents) {
if (nameCounts.containsKey(s.getFirstName())) {
nameCounts.put(s.getFirstName(),
new Integer(nameCounts.get(s.getFirstName()) + 1));
} else {
nameCounts.put(s.getFirstName(), 1);
}
}
String mostCommon = null;
int mostCommonCount = -1;
for (Map.Entry<String, Integer> entry : nameCounts.entrySet()) {
if (mostCommon == null || entry.getValue() > mostCommonCount) {
mostCommon = entry.getKey();
mostCommonCount = entry.getValue();
}
}
return mostCommon;
}
/**
* TODO compute the most common first name out of all students that are no
* longer active in the class using parallel streams. This should mirror the
* functionality of mostCommonFirstNameOfInactiveStudentsImperative. This
* method should not use any loops.
*
* @param studentArray Student data for the class.
* @return Most common first name of inactive students
*/
public String mostCommonFirstNameOfInactiveStudentsParallelStream(
final Student[] studentArray) {
String mostCommon = null;
mostCommon = Stream.of(studentArray).parallel().filter(s -> !s.checkIsCurrent()).map(s->s.getFirstName())
.collect(Collectors.groupingBy(Function.identity(),Collectors.counting())).entrySet()
.stream().max(Comparator.comparing(Map.Entry::getValue))
.get().getKey();
return mostCommon;
}
/**
* Sequentially computes the number of students who have failed the course
* who are also older than 20 years old. A failing grade is anything below a
* 65. A student has only failed the course if they have a failing grade and
* they are not currently active.
*
* @param studentArray Student data for the class.
* @return Number of failed grades from students older than 20 years old.
*/
public int countNumberOfFailedStudentsOlderThan20Imperative(
final Student[] studentArray) {
int count = 0;
for (Student s : studentArray) {
if (!s.checkIsCurrent() && s.getAge() > 20 && s.getGrade() < 65) {
count++;
}
}
return count;
}
/**
* TODO compute the number of students who have failed the course who are
* also older than 20 years old. A failing grade is anything below a 65. A
* student has only failed the course if they have a failing grade and they
* are not currently active. This should mirror the functionality of
* countNumberOfFailedStudentsOlderThan20Imperative. This method should not
* use any loops.
*
* @param studentArray Student data for the class.
* @return Number of failed grades from students older than 20 years old.
*/
public int countNumberOfFailedStudentsOlderThan20ParallelStream(
final Student[] studentArray) {
int count=0;
count= (int) Stream.of(studentArray).parallel().filter(s -> (!s.checkIsCurrent() && s.getGrade()<65 && s.getAge()>20)).count();
return count;
}
}
import java.util.Random;
import junit.framework.TestCase;
import Service.StudentAnalytics;
import Resource.Student;
public class StudentAnalyticsTest extends TestCase {
final static int REPEATS = 10;
private final static String[] firstNames = {"Sanjay", "Yunming", "John", "Vivek", "Shams", "Max"};
private final static String[] lastNames = {"Chatterjee", "Zhang", "Smith", "Sarkar", "Imam", "Grossman"};
private static int getNCores() {
String ncoresStr = System.getenv("COURSERA_GRADER_NCORES");
if (ncoresStr == null) {
return Runtime.getRuntime().availableProcessors();
} else {
return Integer.parseInt(ncoresStr);
}
}
private Student[] generateStudentData() {
final int N_STUDENTS = 2000000;
final int N_CURRENT_STUDENTS = 600000;
Student[] students = new Student[N_STUDENTS];
Random r = new Random(123);
for (int s = 0; s < N_STUDENTS; s++) {
final String firstName = firstNames[r.nextInt(firstNames.length)];
final String lastName = lastNames[r.nextInt(lastNames.length)];
final double age = r.nextDouble() * 100.0;
final int grade = 1 + r.nextInt(100);
final boolean current = (s < N_CURRENT_STUDENTS);
students[s] = new Student(firstName, lastName, age, grade, current);
}
return students;
}
private double averageAgeOfEnrolledStudentsHelper(final int repeats) {
final Student[] students = generateStudentData();
final StudentAnalytics analytics = new StudentAnalytics();
final double ref = analytics.averageAgeOfEnrolledStudentsImperative(students);
final long startSequential = System.currentTimeMillis();
for (int r = 0; r < repeats; r++) {
analytics.averageAgeOfEnrolledStudentsImperative(students);
}
final long endSequential = System.currentTimeMillis();
final double calc = analytics.averageAgeOfEnrolledStudentsParallelStream(students);
final double err = Math.abs(calc - ref);
final String msg = "Expected " + ref + " but found " + calc + ", err = " + err;
assertTrue(msg, err < 1E-5);
final long startParallel = System.currentTimeMillis();
for (int r = 0; r < repeats; r++) {
analytics.averageAgeOfEnrolledStudentsParallelStream(students);
}
final long endParallel = System.currentTimeMillis();
return (double)(endSequential - startSequential) / (double)(endParallel - startParallel);
}
/*
* Test correctness of averageAgeOfEnrolledStudentsParallelStream.
*/
public void testAverageAgeOfEnrolledStudents() {
averageAgeOfEnrolledStudentsHelper(1);
}
/*
* Test performance of averageAgeOfEnrolledStudentsParallelStream.
*/
public void testAverageAgeOfEnrolledStudentsPerf() {
final int ncores = getNCores();
final double speedup = averageAgeOfEnrolledStudentsHelper(REPEATS);
String msg = "Expected parallel version to run at least 1.2x faster but speedup was " + speedup;
assertTrue(msg, speedup > 1.2);
}
private double mostCommonFirstNameOfInactiveStudentsHelper(final int repeats) {
final Student[] students = generateStudentData();
final StudentAnalytics analytics = new StudentAnalytics();
final String ref = analytics.mostCommonFirstNameOfInactiveStudentsImperative(students);
final long startSequential = System.currentTimeMillis();
for (int r = 0; r < repeats; r++) {
analytics.mostCommonFirstNameOfInactiveStudentsImperative(students);
}
final long endSequential = System.currentTimeMillis();
final String calc = analytics.mostCommonFirstNameOfInactiveStudentsParallelStream(students);
assertEquals("Mismatch in calculated values", ref, calc);
final long startParallel = System.currentTimeMillis();
for (int r = 0; r < repeats; r++) {
analytics.mostCommonFirstNameOfInactiveStudentsParallelStream(students);
}
final long endParallel = System.currentTimeMillis();
return (double)(endSequential - startSequential) / (double)(endParallel - startParallel);
}
/*
* Test correctness of mostCommonFirstNameOfInactiveStudentsParallelStream.
*/
public void testMostCommonFirstNameOfInactiveStudents() {
mostCommonFirstNameOfInactiveStudentsHelper(1);
}
/*
* Test performance of mostCommonFirstNameOfInactiveStudentsParallelStream.
*/
public void testMostCommonFirstNameOfInactiveStudentsPerf() {
final int ncores = getNCores();
final double speedup = mostCommonFirstNameOfInactiveStudentsHelper(REPEATS);
final double expectedSpeedup = (double)ncores * 0.5;
String msg = "Expected speedup to be at least " + expectedSpeedup + " but was " + speedup;
assertTrue(msg, speedup >= expectedSpeedup);
}
private double countNumberOfFailedStudentsOlderThan20Helper(final int repeats) {
final Student[] students = generateStudentData();
final StudentAnalytics analytics = new StudentAnalytics();
final int ref = analytics.countNumberOfFailedStudentsOlderThan20Imperative(students);
final long startSequential = System.currentTimeMillis();
for (int r = 0; r < repeats; r++) {
analytics.countNumberOfFailedStudentsOlderThan20Imperative(students);
}
final long endSequential = System.currentTimeMillis();
final int calc = analytics.countNumberOfFailedStudentsOlderThan20ParallelStream(students);
assertEquals("Mismatch in calculated values", ref, calc);
final long startParallel = System.currentTimeMillis();
for (int r = 0; r < repeats; r++) {
analytics.countNumberOfFailedStudentsOlderThan20ParallelStream(students);
}
final long endParallel = System.currentTimeMillis();
return (double)(endSequential - startSequential) / (double)(endParallel - startParallel);
}
/*
* Test correctness of countNumberOfFailedStudentsOlderThan20ParallelStream.
*/
public void testCountNumberOfFailedStudentsOlderThan20() {
countNumberOfFailedStudentsOlderThan20Helper(1);
}
/*
* Test performance of countNumberOfFailedStudentsOlderThan20ParallelStream.
*/
public void testCountNumberOfFailedStudentsOlderThan20Perf() {
final int ncores = getNCores();
final double speedup = countNumberOfFailedStudentsOlderThan20Helper(REPEATS);
String msg = "Expected parallel version to run at least 1.2x faster but speedup was " + speedup;
assertTrue(msg, speedup > 1.2);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment