Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Test Case, Documentation, Refactoring for group_graph.py #261

Merged
merged 131 commits into from
Mar 14, 2019
Merged
Show file tree
Hide file tree
Changes from 130 commits
Commits
Show all changes
131 commits
Select commit Hold shift + click to select a range
4b085b4
Added docstring to test/test_group_graph
baldeosinghm Mar 10, 2019
92acae6
Started work on first test case of test/test_group_graph
baldeosinghm Mar 10, 2019
a42ce2c
Created assertion to compare a group to randomized groups
baldeosinghm Mar 10, 2019
4b27420
Refactored main function to make it easier to test
baldeosinghm Mar 10, 2019
30d8848
Merge pull request #243 from GatorEducator/master
Lancasterwu Mar 10, 2019
8572010
Changed main function
baldeosinghm Mar 10, 2019
e7877aa
Changed exit command to work in init
baldeosinghm Mar 10, 2019
50f0dad
Merge branch 'test/group-graph' of github.com:GatorEducator/gatorgrou…
baldeosinghm Mar 10, 2019
ccab327
Changes made by pull from main branch
baldeosinghm Mar 10, 2019
1b93db5
Imported pytest to use test Exception
baldeosinghm Mar 11, 2019
f64a7f4
Work on test_compatibility_raises, a test for compatibility
baldeosinghm Mar 11, 2019
6a9f68f
A initial test of main. Currently not a priority, but can be looked at
baldeosinghm Mar 11, 2019
085256c
Changed group_graph spacing according to pylint
Mar 11, 2019
f2b025f
Running Black for Formatting
Mar 11, 2019
bcb5723
Added list of student weights to test for test_main
baldeosinghm Mar 11, 2019
8d2eda8
Created 4 different test cases that are not functional at the moment,…
baldeosinghm Mar 11, 2019
40f1383
Merge branch 'test/group-graph' of github.com:GatorEducator/gatorgrou…
baldeosinghm Mar 11, 2019
24b01f3
Fixed pylint error in Travis from a test case
baldeosinghm Mar 11, 2019
2e09109
Created test case for total_cut_size using parameterizing
baldeosinghm Mar 11, 2019
d0d43cf
Use parameterize testing for total_cut_size from group_graph
baldeosinghm Mar 12, 2019
574dd67
Reformatted docstring
baldeosinghm Mar 12, 2019
7e20ec9
Reformatted docstring in group_graph_partition
baldeosinghm Mar 12, 2019
495fd6e
Reformatted main function and added docstring for documentation
baldeosinghm Mar 12, 2019
01081b9
Refactored main function and changed name for easier testing
baldeosinghm Mar 12, 2019
14f73e9
Removed parameterized testing for total_cut_size
baldeosinghm Mar 12, 2019
a44c28c
Rewrote docstring for test_total_cut_size()
baldeosinghm Mar 12, 2019
937905c
Reformated docstrings and began testing of compatibility
baldeosinghm Mar 12, 2019
2c6aaa3
Included comment to remind exception handling is needed for test
baldeosinghm Mar 12, 2019
f4c41c9
Refactored test caes for compatibility length between students
baldeosinghm Mar 12, 2019
d8fc19e
Deleted unused/extraneous code
baldeosinghm Mar 12, 2019
1d2d099
Format Changes
Mar 12, 2019
4f6efec
Changing imports for test_group_graph
Mar 12, 2019
f553615
Correcting Third Party imports
Mar 12, 2019
3149d7e
Reformatting group_graph
Mar 12, 2019
3ff2e5b
Adding to the group_graph_partition docstring
Mar 12, 2019
81cb63b
Specifications to group_graph docstring
Mar 12, 2019
8f433d7
Changing the name of group_graph function so it differs from file name
Mar 12, 2019
2cf452b
Specifying test_recursive_kl docstring
Mar 12, 2019
dbf8970
Dosctring changes
Mar 12, 2019
82fd9d3
Fixed pylint error
baldeosinghm Mar 12, 2019
45274e6
Fixed another pylint error
baldeosinghm Mar 12, 2019
62701cf
Merge branch 'test/group-graph' of github.com:GatorEducator/gatorgrou…
baldeosinghm Mar 12, 2019
234e897
Refactored working test case compatibility_objective_weights()
baldeosinghm Mar 12, 2019
5924342
adding test_group_graph_partition
Mar 12, 2019
b4074d8
Adding a docstring to the test_group_graph_partition test
Mar 12, 2019
a14f202
Adding Expected outputs for test_group_graph_partition
Mar 12, 2019
c276dfe
Adding the assertion to the test case
Mar 12, 2019
18d2b73
Adding parameters to test_group_graph_partition
Mar 12, 2019
ffbc073
Running black for group_graph.py
Mar 12, 2019
4dd02d4
Refined test case test_compatability_objective_measures
baldeosinghm Mar 12, 2019
23ae020
Merge different local changes to test/group-graph
baldeosinghm Mar 12, 2019
4d4d839
Change docstring of test_compatibility_objective_weights()
baldeosinghm Mar 12, 2019
b313daa
Change docstring of test_compatibility_objective_measures()
baldeosinghm Mar 12, 2019
debfd49
Remove comment indicating improvement of test case test_total_cut_size
baldeosinghm Mar 12, 2019
34303d1
Remove another comment
baldeosinghm Mar 12, 2019
7e52173
Added necessary test cases for group_graph.py
baldeosinghm Mar 12, 2019
8c9580d
Change docstring of test_recursive_kl
baldeosinghm Mar 12, 2019
094f8c0
Add test case for recursive_kl()
baldeosinghm Mar 12, 2019
c37b926
Add docstring for test_recursive_kl_multi()
baldeosinghm Mar 12, 2019
2b5a9aa
Finish test cases for recursive_kl()
baldeosinghm Mar 12, 2019
bbe9cc3
Fixed pylint issue regarding imports
Mar 12, 2019
362a9df
Changed the equality sign to in for assertion
Mar 12, 2019
71265ab
Changed variable input because its a predefined function
Mar 12, 2019
7c6f5a1
Final pylint change
Mar 12, 2019
7dea7b7
Refactor assert statement to pass pylint and pytest
Mar 12, 2019
207934a
Merge pull request #258 from GatorEducator/master
Lancasterwu Mar 13, 2019
cb1aa6b
Add documentation for recursive_kl()
baldeosinghm Mar 13, 2019
0946208
Add documentation for total_cut_size()
baldeosinghm Mar 13, 2019
56a0380
Add documentation for compatability
baldeosinghm Mar 13, 2019
d1824e1
Removing main method from group_graph
Mar 13, 2019
d5d83bf
Remove incorrect assertion
Mar 13, 2019
448bff6
Adding a dictionary to check the preferences of students
Mar 13, 2019
9ee64c4
Declaring the output of the function
Mar 13, 2019
123ff07
Adding the dictionary to the parameters
Mar 13, 2019
9721025
Adding the assertion to the test
Mar 13, 2019
4dc3878
Fixing indentation of test
Mar 13, 2019
bb55ecd
Fixing dictionary for int not iterable error
wattob Mar 13, 2019
569bf0e
Adding len to fix assertion error
wattob Mar 13, 2019
1aeaa52
Updating Dictionary to cover line 137
wattob Mar 13, 2019
f8d2046
Updating docstring for test_group_graph_partition function
wattob Mar 13, 2019
034dc3c
Running black for the formatting of the test case
Mar 13, 2019
e9f0c6f
Edits to follow Pylint
wattob Mar 13, 2019
ca35fc5
Refactor compatability test case to handle raise Exception
baldeosinghm Mar 13, 2019
af799f7
Merge branch 'test/group-graph' of github.com:GatorEducator/gatorgrou…
baldeosinghm Mar 13, 2019
6e316af
Delete unecessary print statement
baldeosinghm Mar 14, 2019
016cc58
Add exception raising to compatibility function
baldeosinghm Mar 14, 2019
b67aeb0
Fix exception raising for recursive_kl function
baldeosinghm Mar 14, 2019
3fbbcc3
Delete unecessary print statement
baldeosinghm Mar 14, 2019
1befc9a
Refactor exception raising for compatibility length
baldeosinghm Mar 14, 2019
b90af4d
Add test case for callable function in compatibility
baldeosinghm Mar 14, 2019
5a28583
Refactor match test case for compatibility
baldeosinghm Mar 14, 2019
74bf7dd
Add test case for raising error for inappropriate measure
baldeosinghm Mar 14, 2019
4c9522a
Correctly adds set of preferred student preferences
baldeosinghm Mar 14, 2019
86c37d0
Add docstring to test_compatibility_measure_preset
baldeosinghm Mar 14, 2019
88e7d02
Merge branch 'master' into test/group-graph
baldeosinghm Mar 14, 2019
706e047
Fix spelling of compatibility in all function declarations
baldeosinghm Mar 14, 2019
3898603
Merge branch `test/group-graph` of github.com:GatorEducator/gatorgrou…
baldeosinghm Mar 14, 2019
791de2f
Merge branch 'master' into test/group-graph
Lancasterwu Mar 14, 2019
8b8d80b
Clearify the docstring for test_group_graph_partition
Lancasterwu Mar 14, 2019
d068f77
Removed unnessesary testing code
Lancasterwu Mar 14, 2019
262c4e0
Add a new default for preference
Lancasterwu Mar 14, 2019
d2a903d
generate a new parser to read the preference
Lancasterwu Mar 14, 2019
add9a21
Call preferences in gatorgrouper_cli
Lancasterwu Mar 14, 2019
14550c8
error that the readcsv function would be able to read students name a…
Lancasterwu Mar 14, 2019
a9df671
Fixed a bug that elif in readstudent file was not check if it is float
Lancasterwu Mar 14, 2019
2b6aced
Fixed a bug that elif statement should include .isdigit otherwise 0 a…
Lancasterwu Mar 14, 2019
8dc63be
test statement
Lancasterwu Mar 14, 2019
3781040
preference should be dictionary since .get can not be used on string
Lancasterwu Mar 14, 2019
55d6ca7
remove testing printline
Lancasterwu Mar 14, 2019
cb0bdd7
Added new default value for preference weight and preference weight m…
Lancasterwu Mar 14, 2019
69787a2
Added the new parser for both preference weight and preference weight…
Lancasterwu Mar 14, 2019
9bf5102
Added these two new command to gatorgrouper_cli.py
Lancasterwu Mar 14, 2019
3b3b3e5
Reformate gatorgrouper_cli.py
Lancasterwu Mar 14, 2019
991c3eb
Increase the test coverage
Lancasterwu Mar 14, 2019
aacdbba
Fixed linting errors
Lancasterwu Mar 14, 2019
575bcb5
remove importing json since we are not using it
Lancasterwu Mar 14, 2019
469bc41
Changed the -help for new parsers
Lancasterwu Mar 14, 2019
fdec943
Implementing the last two parameters for group graph
Lancasterwu Mar 14, 2019
7642d63
Added objective measures and objective weights to the main cli program
Lancasterwu Mar 14, 2019
9a04e12
ran black
Lancasterwu Mar 14, 2019
953207b
Merge branch 'master' into test/group-graph
Lancasterwu Mar 14, 2019
5759b9a
Add detail explaination of Kernighan-Lin Grouping Method
baldeosinghm Mar 14, 2019
7a834fc
Add documentation for --method graph
baldeosinghm Mar 14, 2019
19edb17
Add documentation of --preferences
baldeosinghm Mar 14, 2019
96a134b
Add documentation for --preferences_weight
baldeosinghm Mar 14, 2019
a81ef76
Add documentation for --preferences_weight_match
baldeosinghm Mar 14, 2019
c68e520
Add documentation for --objective_measures
baldeosinghm Mar 14, 2019
d5a1d80
Add documentation for --objective_weights
baldeosinghm Mar 14, 2019
980244c
Add documentation for all arguments in command line
baldeosinghm Mar 14, 2019
7b4b500
Merge branch `test\group-graph` of github.com:GatorEducator/gatorgrou…
baldeosinghm Mar 14, 2019
1a24c04
Fixed mdl error
baldeosinghm Mar 14, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,71 @@ pipenv run python3 gatorgrouper_cli.py --debug

If neither of these flags are set, logging will only be shown if an error occurs.

### Kernighan-Lin Grouping Method

The Kernighan-Lin algorithm creates a k-way graph partition that determines the
grouping of students based on their preferences for working with other students
and compatibility with other classmates. The graph recognizes student compatibility
through numerical weights (indicators of student positional relationship on the graph).
This grouping method allows for a systematic approach and balanced number of student
groups capable of tackling different types of work. Students should enter student name,
number of groups, objective weights (optional), objective_measures(optional), students preferred to work with (optional), preference weight(optional), and preferences_weight_match(optional). Note that number of groups must be at least 2
and be a power of 2, i.e. 2, 4, 8...

NOTE: `--method graph` and `--num-group` are required to create groups.

It is required to use the graph argument to generate groups through the graph partitioning.
To generate groups using the Kernighan-Lin grouping algorithm use the flag `--method graph`

```shell
pipenv run python gatorgrouper_cli.py --file filepath --method graph --num-group NUMBER
```

To load student preferences, a preference weight, use the flag `--preferences`

```shell
pipenv run python gatorgrouper_cli.py --file filepath --method graph --num-group NUMBER
--preferences filepath
```

To indicate student preference weight use the flag `--preferences_weight`

```shell
pipenv run python gatorgrouper_cli.py --file filepath --method graph --num-group NUMBER
--preferences filepath --preferences_weight PREFERENCES_WEIGHT
```

To indicate preference weight match use the flag `--preferences_weight_match`

```shell
pipenv run python gatorgrouper_cli.py --file filepath --method graph --num-group NUMBER
--preferences filepath --preferences_weight PREFERENCES_WEIGHT --preferences_weight_match
PREFERENCES_WEIGHT_MATCH
```

To add objective measures use the flag `--objective_measures`

```shell
pipenv run python gatorgrouper_cli.py --file filepath --method graph --num-group NUMBER
--objective_measures LIST --objective_weights LIST
```

To add objective weights use the flag `--objective_weights`

```shell
pipenv run python gatorgrouper_cli.py --file filepath --method graph --num-group NUMBER
--objective_measures LIST --objective_weights LIST
```

A command line of all agruments would be:

```shell
pipenv run python gatorgrouper_cli.py --file filepath --method graph --num-group NUMBER
--preferences filepath --preferences-weight PREFERENCES_WEIGHT --preferences-weight-match
PREFERENCES_WEIGHT_MATCH --objective-measures LIST --objective-weights LIST
```


### Full Example

```shell
Expand Down
5 changes: 5 additions & 0 deletions gatorgrouper/utils/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
DEFAULT_GRPSIZE = 3
DEFAULT_NUMGRP = 3
DEFAULT_ABSENT = ""
DEFAULT_PREFERENCES = None
DEFAULT_PREFERENCES_WEIGHT = 1.1
DEFAULT_PREFERENCES_WEIGHT_MATCH = 1.3
DEFAULT_OBJECTIVE_WEIGHTS = None
DEFAULT_OBJECTIVE_MEASURES = None

# assertion
NONE = ""
Expand Down
33 changes: 15 additions & 18 deletions gatorgrouper/utils/group_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@

def recursive_kl(graph: Graph, numgrp=2) -> List[Set[int]]:
"""
Recursively use Kernighan-Lin algorithm to create a k-way graph partition
Recursively use the Kernighan-Lin algorithm to create a k-way graph partition.
This function will either return two groups or more than two depending on the
value of numgrp. Each group generated is different from the previous.
"""
power = log(numgrp, 2)
if power != int(power) or power < 1:
raise ValueError("numgrp must be a power of 2 and at least 2.")
# For a group of two bisect it and return two groups
if numgrp == 2:
# Base case for recursion: use Kernighan-Lin to create 2 groups
return list(kernighan_lin_bisection(graph))
# For the next group of two divide numgrp by 2
next_numgrp = numgrp / 2
groups = []
for subset in kernighan_lin_bisection(graph):
Expand All @@ -31,10 +35,11 @@ def total_cut_size(graph: Graph, partition: List[int]) -> float:
Computes the sum of weights of all edges between different subsets in the partition
"""
cut = 0.0
# Edges are added from the nodes on the graph, creating subsets
for i, subset1 in enumerate(partition):
for subset2 in partition[i:]:
# Sum of weights added from all subsets and set equal to cut
cut += cut_size(graph, subset1, T=subset2)
print(subset1, subset2, cut)
return cut


Expand All @@ -58,10 +63,13 @@ def compatibility(
If no measures are specified, "avg" is used as a default.
"""
if not len(a) == len(b):
raise Exception("Tuples passed to compatibility() must have same size")
# Raise an exception notice if student tuples don't match
raise Exception("Tuples passed to compatibility() must have same size.")
if objective_weights is None:
# Return length
objective_weights = [1] * len(a)
if objective_measures is None:
# Default to return average if set equal to None
objective_measures = ["avg"] * len(a)
scores = []
for a_score, b_score, weight, measure in zip(
Expand All @@ -80,6 +88,8 @@ def compatibility(
compat = int(a_score == b_score)
elif measure == "diff":
compat = abs(a_score - b_score)
else:
raise Exception("Invalid measure")

# Scale the compatibility of a[i] and b[i] using the i-th objective weight
scores.append(compat * weight)
Expand All @@ -96,7 +106,8 @@ def group_graph_partition(
preferences_weight_match=1.3,
):
"""
Form groups using recursive Kernighan-Lin algorithm
Form groups using recursive Kernighan-Lin algorithm by reading in students list
and weight list and partitioning the vertices.
"""
# Read in students list and the weight list
students = [item[0] for item in inputlist]
Expand Down Expand Up @@ -133,17 +144,3 @@ def group_graph_partition(
for p in partition:
groups.append([inputlist[i] for i in p])
return groups


if __name__ == "__main__":
student_data = [
["one", 0, 0],
["two", 0, 0.5],
["three", 0.5, 0],
["four", 0.75, 0.75],
["five", 0.8, 0.1],
["six", 0, 1],
["seven", 1, 0],
["eight", 1, 1],
]
student_groups = group_graph_partition(student_data, 4)
40 changes: 40 additions & 0 deletions gatorgrouper/utils/parse_arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,46 @@ def parse_arguments(args):
required=False,
)

gg_parser.add_argument(
"--preferences",
help="Preferences of students for graph algorithm",
type=str,
default=constants.DEFAULT_PREFERENCES,
required=False,
)

gg_parser.add_argument(
"--preferences-weight",
help="Prefered weights",
type=float,
default=constants.DEFAULT_PREFERENCES_WEIGHT,
required=False,
)

gg_parser.add_argument(
"--preferences-weight-match",
help="Prefered matching weights",
type=float,
default=constants.DEFAULT_PREFERENCES_WEIGHT_MATCH,
required=False,
)

gg_parser.add_argument(
"--objective-weights",
help="Objective weights for compatibility input csv file",
type=list,
default=constants.DEFAULT_OBJECTIVE_WEIGHTS,
required=False,
)

gg_parser.add_argument(
"--objective-measures",
help="Objective measures for compatibility input csv file: sum, avg, max, min, match, diff",
type=list,
default=constants.DEFAULT_OBJECTIVE_MEASURES,
required=False,
)

gg_arguments_finished = gg_parser.parse_args(args)

logging.basicConfig(
Expand Down
9 changes: 7 additions & 2 deletions gatorgrouper/utils/read_student_file.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
""" Reads CSV data file """

import csv
import re
from pathlib import Path


Expand Down Expand Up @@ -28,8 +29,10 @@ def read_csv_data(filepath):
temp.append(True)
elif value.lower() == "false":
temp.append(False)
else:
elif re.match(r"^\d+?\.\d+?$", value) or value.isdigit():
temp.append(float(value))
else:
temp.append(value)
responses.append(temp)
else:
for record in csvdata:
Expand All @@ -40,7 +43,9 @@ def read_csv_data(filepath):
temp.append(True)
elif value.lower() == "false":
temp.append(False)
else:
elif re.match(r"^\d+?\.\d+?$", value) or value.isdigit():
temp.append(float(value))
else:
temp.append(value)
responses.append(temp)
return responses
9 changes: 8 additions & 1 deletion gatorgrouper_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

# read in the student identifiers from the specified file
input_list = read_student_file.read_csv_data(GG_ARGUMENTS.file)
preference = dict(read_student_file.read_csv_data(GG_ARGUMENTS.preferences))
check_if_arguments_valid = parse_arguments.check_valid(GG_ARGUMENTS, input_list)
if check_if_arguments_valid is False:
print("Incorrect command-line arguments.")
Expand Down Expand Up @@ -53,7 +54,13 @@
)
elif GG_ARGUMENTS.method == constants.ALGORITHM_GRAPH:
GROUPED_STUDENT_IDENTIFIERS = group_graph.group_graph_partition(
SHUFFLED_STUDENT_IDENTIFIERS, GG_ARGUMENTS.num_group
SHUFFLED_STUDENT_IDENTIFIERS,
GG_ARGUMENTS.num_group,
preferences=preference,
preferences_weight=GG_ARGUMENTS.preferences_weight,
preferences_weight_match=GG_ARGUMENTS.preferences_weight_match,
objective_weights=GG_ARGUMENTS.objective_weights,
objective_measures=GG_ARGUMENTS.objective_measures,
)
else:
GROUPED_STUDENT_IDENTIFIERS = group_creation.group_random_num_group(
Expand Down
15 changes: 11 additions & 4 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,19 @@ def generate_csv_no_header(tmpdir_factory):
def generate_csv_float(tmpdir_factory):
""" Generate a tempory sample csv """
fn = tmpdir_factory.mktemp("data").join("csvNg.csv")
headers = ["NAME", "Q1", "Q2", "Q3", "Q4"]
headers = ["NAME", "Q1", "Q2", "Q3", "Q4", "Q5"]
with open(str(fn), "w") as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=headers)
writer.writeheader()
writer.writerow(
{"NAME": "delgrecoj", "Q1": "1.2", "Q2": "1.1", "Q3": "0.9", "Q4": "2.3"}
{
"NAME": "delgrecoj",
"Q1": "1.2",
"Q2": "1.1",
"Q3": "0.9",
"Q4": "2.3",
"Q5": "Name",
}
)
return str(fn)

Expand All @@ -113,8 +120,8 @@ def generate_csv_float_no_header(tmpdir_factory):
fn = tmpdir_factory.mktemp("data").join("csvNg1.csv")
data = [
# optionally include headers as the first entry
["delgrecoj", "1.2", "0.7", "1.1", "0.2"],
["delgrecoj2", "0.1", "0.5", "0.8", "0.6"],
["delgrecoj", "1.2", "0.7", "1.1", "0.2", "Name"],
["delgrecoj2", "0.1", "0.5", "0.8", "0.6", "Name"],
]
csv_string = ""
for entry in data:
Expand Down
Loading