/// <summary> /// Filters loaded data based on a given criteria set /// </summary> /// <param name="vm"></param> private void FilterStudentsByGrade(TimelineAnalysisViewModel vm) { //present options to filter by List <string> gradingCriteria = vm.GetAllGrades(); Console.WriteLine("***Grading Categories***"); for (int i = 0; i < gradingCriteria.Count; i++) { Console.WriteLine("{0}: {1}", i, gradingCriteria[i]); } Console.Write("Select category: "); string categoryStr = Console.ReadLine(); //ask for min/max filter Console.Write("Enter max score [100%]: "); string maxScoreStr = Console.ReadLine(); Console.Write("Enter min score [0%]: "); string minScoreStr = Console.ReadLine(); //parse strings, send off to VM for processing int category = -1; double maxScore = 200; double minScore = 0; if (Int32.TryParse(categoryStr, out category) && category > -1) { if (Double.TryParse(maxScoreStr, out maxScore) == false) { maxScore = 200; } if (Double.TryParse(minScoreStr, out minScore) == false) { minScore = 0; } int result = vm.FilterByGrade(gradingCriteria[category], minScore, maxScore); Console.WriteLine("{0} entries removed from dataset.", result); } else { Console.WriteLine("Error parsing user input."); } }
/// <summary> /// Creates a matrix of cycle activity for each student /// </summary> /// <param name="vm"></param> private void OrderTransitionsByDate(TimelineAnalysisViewModel vm) { //step 1: get list of files to process List <string> filesToProcess = new List <string>(); string fileName = "a"; Console.WriteLine("Enter files to process (-1 to stop)"); while ((fileName = GetFile()).Length > 0) { filesToProcess.Add(fileName); } //step 2: setup grade-bands (e.g. A, B, C, etc.) Hard coded for now as this is just for a //single class double maxScore = 200; double[] gradeRanges = { 90, 78, 69, 60, 0 }; string[] gradeMap = { "A", "B", "C", "D", "F" }; //this produces a lot of files, so create a separate directory for the output string outputDirectory = "TransitionsByDate"; if (Directory.Exists(outputDirectory) == false) { Directory.CreateDirectory(outputDirectory); } //finally, begin processing //reset max score for A students maxScore = 200; //based on currently existing code, it is easier to reopen the file for //each grade range for (int i = 0; i < gradeRanges.Length; i++) { double bound = gradeRanges[i]; //reload the files LoadFile(filesToProcess[0]); for (int j = 1; j < filesToProcess.Count; j++) { vm.AppendTimeline(filesToProcess[j]); } //get grade data vm.AttachGrades(); //filter based on grade data vm.FilterByGrade("Assignment AVG", bound, maxScore); //get transitions for this grade level var result = vm.OrderTransitionsByDate(); var byDate = result.Item1; //update scores for next grade boundary maxScore = bound - 0.01; //hold all day keys for easier access int[] keys = byDate.Keys.OrderBy(k => k).ToArray(); //figure out all transitions Dictionary <string, string> transitionsDict = new Dictionary <string, string>(); foreach (int key in keys) { foreach (string transition in byDate[key].Keys) { transitionsDict[transition] = transition; } } string[] transitions = transitionsDict.Keys.ToArray(); //write aggregate information to a file CsvWriter writer = new CsvWriter(); //blank line for transitions writer.AddToCurrentLine("Transition"); //add in header row foreach (int key in keys) { writer.AddToCurrentLine(key); } writer.CreateNewRow(); //add in data foreach (string transition in transitions) { //data for given transition writer.AddToCurrentLine("T: " + transition); foreach (int key in keys) { if (byDate[key].ContainsKey(transition)) { //add in data for given transition writer.AddToCurrentLine(byDate[key][transition].Count); } else { //no data, add a 0 writer.AddToCurrentLine(0); } } writer.CreateNewRow(); } //aggregate class results using (TextWriter tw = File.CreateText(string.Format("{0}/aggregate_{1}.csv", outputDirectory, gradeMap[i]))) { tw.Write(writer.ToString()); Console.WriteLine("Created file aggregate_{0}.csv", gradeMap[i]); } //write individual student information to file writer = new CsvWriter(); writer.AddToCurrentLine("UserID"); //blank line for transitions writer.AddToCurrentLine("Transition"); //add in header row foreach (int key in keys) { writer.AddToCurrentLine(key); } writer.CreateNewRow(); //add in data var userData = result.Item2; foreach (int userId in userData.Keys) { foreach (string transition in transitions) { //user id writer.AddToCurrentLine(userId); //data for given transition writer.AddToCurrentLine("T: " + transition); foreach (int key in keys) { if (userData[userId][key].ContainsKey(transition)) { //add in data for given transition writer.AddToCurrentLine(userData[userId][key][transition].Count); } else { //no data, add a 0 writer.AddToCurrentLine(0); } } writer.CreateNewRow(); } } using (TextWriter tw = File.CreateText(string.Format("{0}/students_{1}.csv", outputDirectory, gradeMap[i]))) { tw.Write(writer.ToString()); Console.WriteLine("Created file students_{0}.csv", gradeMap[i]); } } }
/* * What I want to do: * For each assignment: * figure out common sequences of length m to n * For each student, for each grade band (A-F), again determine frequences of length m to n * Build a frequency distribution for each grade band by sequence * */ private void BuildTransitionFrequencyCounts(TimelineAnalysisViewModel vm) { //step 1: get list of files to process List <string> filesToProcess = new List <string>(); string fileName = "a"; Console.WriteLine("Enter files to process (-1 to stop)"); while ((fileName = GetFile()).Length > 0) { filesToProcess.Add(fileName); } //step 2: setup grade-bands (e.g. A, B, C, etc.) Hard coded for now as this is just for a //single class double maxScore = 200; double[] gradeRanges = { 90, 78, 69, 60, 0 }; string[] gradeMap = { "A", "B", "C", "D", "F" }; //step 3: get sequence boundaries. Again, hard coded for now int startingSequenceLength = 2; int endingSequenceLength = 25; //step 4: get assignments. string[] assignments = { "Assignment #1", "Assignment #2", "Assignment #3", "Assignment #4", "Assignment #5", "Assignment #6", "Assignment #7" }; int assignmentCounter = 0; //this produces a lot of files, so create a separate directory for the output string outputDirectory = "TransitionFrequencyCounts"; if (Directory.Exists(outputDirectory) == false) { Directory.CreateDirectory(outputDirectory); } //finally, begin processing foreach (string fileToProcess in filesToProcess) { string folderName = fileToProcess.Replace("#", ""); string outputPath = Path.Combine(outputDirectory, folderName); if (Directory.Exists(outputPath) == false) { Directory.CreateDirectory(outputPath); } for (int sequenceLength = startingSequenceLength; sequenceLength <= endingSequenceLength; sequenceLength++) { //reset max score for A students maxScore = 200; //based on currently existing code, it is easier to reopen the file for //each grade range for (int i = 0; i < gradeRanges.Length; i++) { double bound = gradeRanges[i]; //reload the file LoadFile(fileToProcess); //get grade data vm.AttachGrades(); //filter based on grade data vm.FilterByGrade(assignments[assignmentCounter], bound, maxScore); //update scores for next grade boundary maxScore = bound - 0.01; //build markov transitions vm.BuildDefaultMarkovStates(); //figure out sequence distribution for entire data set and for individual students Dictionary <string, int> transitions = vm.GetAllTransitionCombinations(sequenceLength); //interesting transitions are those in which we have at least 5 occurrances var interestingTransitions = transitions.Where(t => t.Value > 5).OrderBy(t => t.Value).ToList(); //write this information to a file CsvWriter writer = new CsvWriter(); //aggregate class results Console.WriteLine("Processing transition sequences of length {0}...", sequenceLength); foreach (KeyValuePair <string, int> kvp in interestingTransitions) { writer.AddToCurrentLine(kvp.Key); writer.AddToCurrentLine(kvp.Value.ToString()); writer.CreateNewRow(); } using (TextWriter tw = File.CreateText(string.Format("{0}/aggregate_{1}_{2}.csv", outputPath, sequenceLength, gradeMap[i]))) { tw.Write(writer.ToString()); } //individual students //add header data writer = new CsvWriter(); writer.AddToCurrentLine("UserId"); writer.AddToCurrentLine("Grade"); foreach (var kvp in interestingTransitions) { writer.AddToCurrentLine(kvp.Key); } writer.CreateNewRow(); foreach (var user in vm.Timeline.Values) { //first row for users is raw values writer.AddToCurrentLine(user.OsbideId); writer.AddToCurrentLine(gradeMap[i]); //only use the interesting states as columns as identified in the aggregate analysis foreach (KeyValuePair <string, int> kvp in interestingTransitions) { if (user.TransitionCounts.ContainsKey(kvp.Key) == true) { writer.AddToCurrentLine(user.TransitionCounts[kvp.Key]); } else { writer.AddToCurrentLine("0"); } } writer.CreateNewRow(); //2nd row contains normalized values writer.AddToCurrentLine(user.OsbideId); writer.AddToCurrentLine(gradeMap[i]); int totalTransitions = user.TransitionCounts.Values.Sum(); //only use the interesting states as columns as identified in the aggregate analysis foreach (KeyValuePair <string, int> kvp in interestingTransitions) { if (user.TransitionCounts.ContainsKey(kvp.Key) == true) { writer.AddToCurrentLine(user.TransitionCounts[kvp.Key] / (double)totalTransitions); } else { writer.AddToCurrentLine("0"); } } writer.CreateNewRow(); } using (TextWriter tw = File.CreateText(string.Format("{0}/individual_{1}_{2}.csv", outputPath, sequenceLength, gradeMap[i]))) { tw.Write(writer.ToString()); } } } //move to the next assignment assignmentCounter++; } }