Repository: oppia/oppia

Issue:
The visualization of Image Click Input answers in the stats tab looks bad
For image click interactions, the visualization shown in the state statistics modal in the stats tab of the exploration editor is unusable. It shows a histogram with the frequency of each answer. The problem with this is that each answer is represented by the coordinates of the click, which leads to the following being shown:

![screenshot from 2017-10-08 02-14-56](https://user-images.githubusercontent.com/10575562/31315486-8aeddac6-abce-11e7-955c-43eb3380cafa.png)

To reproduce:

1. Create an exploration locally with an image click interaction.
2. As a learner, submit some answers by clicking on various parts of the image.
3. Log in as an admin, go to /admin, go to the Jobs tab and start the InteractionAnswerSummariesAggregator continuous computation. Wait for it to finish running at least once.
4. Go to the stats tab in the exploration editor, and click on a state to view its stats. (Ignore the "Times entered: 0" part -- that's a separate issue that we're currently investigating.) You'll see a picture similar to the above. It gets worse as more answers are submitted.

If you'd like to take up this issue, please explain your proposed design here before doing any coding. It probably makes sense to do some clustering based on the region of the image that was clicked, but this makes it hard to handle cases where people click outside any of the predefined regions (which does happen, and can sometimes indicate that there's something confusing about the way the image is structured). Perhaps we might want to show a histogram by labelled regions (with an extra bar for "clicks outside any region"), as well as a heatmap or a set of points that give the creator some sense of the overall distribution of the clicks over the image.


Discussion:
By the way, we were also discussing other possible ways of expressing this. A heat map may not be the most creator-friendly way of expressing this.

In this section of the [doc](https://docs.google.com/document/d/11ZAe7BZqYsCS60oIDwvXQ7xKH4wwrrffN3HyqsNFbAw/edit#bookmark=id.q0twh4q9dzi4), we have expressed two ways of doing this. 
The first one was to crop images into their regions, but this could potentially clutter the stats tab window. 
The alternative was to display the original image once at the top and draw over it splitting it into regions - that way, mentioning Region 1 would be enough.

/cc @seanlip 
The alternative method of displaying the entire image in the stats modal as suggested by @pranavsid98 in this [doc](https://docs.google.com/document/d/11ZAe7BZqYsCS60oIDwvXQ7xKH4wwrrffN3HyqsNFbAw/edit#bookmark=id.q0twh4q9dzi4) seems the most apt imo. I do have a suggestion though. 
For eg, instead of displaying the region name on the bounding rectangles and then relating that to a bar chart, I think it would be better if we were to display a bubble chart overlayed (perhaps with low opacity) on top of the image, with bubbles (whose sizes depend on the number of clicks) are displayed atop that particular region.
This solution would be more creator-friendly than, say, a heatmap and wouldn't need a seperate chart alongside the image as with the bar chart idea.

/cc @seanlip
Hi @iamprayush, thanks for the suggestion! Can you provide a mock/image to illustrate what you're talking about, just to ensure that we're on the same page?

Also, I think @pranavsid98 @brianrodri and @swaat-on-git would probably be better folks to give comments on this than I am, especially @swaat-on-git because she is working on updated designs for the different interaction types.
@seanlip I had something like this in mind:

![25128396](https://user-images.githubusercontent.com/35144226/73150487-99804000-40ec-11ea-9707-8e2a836e379e.jpg)

Here the bubble sizes are directly proportional to the number of clicks in that region.

@pranavsid98 @brianrodri @swaat-on-git Do suggest any changes or alternatives, I would love to work on this issue. Thanks!

Hm, at first glance, this seems a bit confusing, right? The diagram mixes up the location of the region and the frequency of clicks, so I can't tell whether a big circle represents a big region or a large number of clicks.
> Hm, at first glance, this seems a bit confusing, right? The diagram mixes up the location of the region and the frequency of clicks, so I can't tell whether a big circle represents a big region or a large number of clicks.

@seanlip Yes, that would be an issue now that I think about it. 
Another idea I had in mind was to show bounding boxes of each region and then the creator can hover over the box and the information about the click frequency can be revealed in the form of 
 the percentage of clicks in that region or some other visual aid.

But, if that doesn't seem appealing, then I think, the alternative method in this [doc](https://docs.google.com/document/d/11ZAe7BZqYsCS60oIDwvXQ7xKH4wwrrffN3HyqsNFbAw/edit#bookmark=id.q0twh4q9dzi4) seems to be the way to go. This is a rough idea of what I understood from the explanation:

![25128396 jpg](https://user-images.githubusercontent.com/35144226/73476138-b603cd00-43b7-11ea-8d5f-e7e78916005a.png)

Thanks, @iamprayush. Let's get clarification from @swaat-on-git -- she has some thoughts on this and it would be good to loop her in to the conversation.

@iamprayush There is a similar view being built for improvements tab, where we show the distribution of "Most frequent wrong answers" in different content types. The image view is currently in progress, and I'll update the version when it is in a shareable stage. You might be able to reuse the component in stats tab as well.

@seanlip 
@swaat-on-git That sounds great! Do let me know if there is anything I can help with. Thanks :)
@iamprayush @seanlip @pranavsid98 Here's an idea for how we can show the activity on image type of input - 
Here, we show both clicks inside regions mapped and addressed by the creator, and clicks outside such regions, so he may be aware that there are other regions that need to be addressed. The cluster size essentially makes the click areas bigger or smaller based on the slider. This is done by grouping the click points that are the close to each other to form a single section. Here's some [references for clustering](https://towardsdatascience.com/the-5-clustering-algorithms-data-scientists-need-to-know-a36d136ef68)
![image](https://user-images.githubusercontent.com/56828144/76285211-2e1db680-62c5-11ea-833b-fc684a590796.png)

As making these unmapped regions are a big effort, for the first version we could address just the pre-defined regions-
![image](https://user-images.githubusercontent.com/56828144/76285473-e2b7d800-62c5-11ea-8f88-c9b65d8d67a0.png)

What do you think?


Sorry pasted the wrong image for the version 1 - with only pre-defined regions
![image](https://user-images.githubusercontent.com/56828144/76285658-4b9f5000-62c6-11ea-9af9-548052c78c80.png)
@seanlip @brianrodri @pranavsid98 @iamprayush 
Thanks, I think it looks good @swaat-on-git!

(Also, as an FYI for the future, it is possible for you to edit your previous comments on GitHub.)
@swaat-on-git I just have a minor doubt. The unmapped clusters would probably not overlap with each other but is it possible that they might overlap with the pre-defined ones? If so, we might want to restrict that in order to avoid any confusion.
Otherwise, it looks great :)
@iamprayush 
They will always remain mutually exclusive and this is why-
Every click is mapped to a coordianate in the image. This coordinate is first checked if it is part of a pre-defined area. 
If yes, the count gets added to that area. 
If not, it is an unmapped region and is upto the algorithm to use this click while forming clusters for this region.
Hope this helps!

Also my understanding is that we will implement only the pre-defined area design in the first version. Here's how this version will behave-

By default it looks this way
![image](https://user-images.githubusercontent.com/56828144/76588401-f2752d80-650c-11ea-930b-708d48636ac9.png)

When the user hovers on either the stat / or over a region in the image, the pair gets highlighted
![image](https://user-images.githubusercontent.com/56828144/76588451-10db2900-650d-11ea-944f-746ef45403a5.png)
@seanlip  Is this sufficiently highlighted?
@swaat-on-git I understand now. Thanks for the clarification :)
While looking into ways to implement this, I came across a hex-bin algorithm: https://github.com/d3/d3-hexbin.

@swaat-on-git, how do you feel about using hexagonal regions instead of rectangular ones? For example:
![image](https://user-images.githubusercontent.com/5094060/81488995-6ba7ad00-923e-11ea-8120-884e32504f64.png)

The benefits of this strategy would be not needing to differentiate between regions (all clicks would fall into a hexagon "bucket"). WDYT?
If having it as hexagons makes the algorithm easier, let's go for it! Only thing is, when users select regions to pre-define them while creating the exploration, the same shape should be used to maintain consistency. That way, answers are always associated with hexagons.

Resolving patch:
diff --git a/extensions/interactions/ImageClickInput/ImageClickInput.py b/extensions/interactions/ImageClickInput/ImageClickInput.py
--- a/extensions/interactions/ImageClickInput/ImageClickInput.py
+++ b/extensions/interactions/ImageClickInput/ImageClickInput.py
@@ -61,14 +61,11 @@ class ImageClickInput(base.BaseInteraction):
 
     _answer_visualization_specs = [{
         # Bar chart with answer counts.
-        'id': 'BarChart',
-        'options': {
-            'x_axis_label': 'Answer',
-            'y_axis_label': 'Count',
-        },
+        'id': 'ClickHexbins',
+        'options': {},
         'calculation_id': 'AnswerFrequencies',
-        # Bar charts don't have any useful way to display which answers are
-        # addressed yet. By setting this option to False, we consequentially
-        # avoid doing extra computation.
+        # Adding addressed info for hexbins needs more design work, but
+        # conceptually should be possible by highlighting which hexagons are
+        # explicitly addressed.
         'addressed_info_is_supported': False,
     }]
diff --git a/extensions/visualizations/models.py b/extensions/visualizations/models.py
--- a/extensions/visualizations/models.py
+++ b/extensions/visualizations/models.py
@@ -94,6 +94,25 @@ class BarChart(BaseVisualization):
     }]
 
 
+class ClickHexbins(BaseVisualization):
+    """A visualization which overlays an image with a hexagonal grouping of
+    clicks.
+
+    > Why hexagons? There are many reasons for using hexagons, at least over
+      squares. Hexagons have symmetry of nearest neighbors which is lacking in
+      square bins. Hexagons are the maximum number of sides a polygon can have
+      for a regular tesselation of the plane, so in terms of packing a hexagon
+      is 13% more efficient for covering the plane than squares. This property
+      translates into better sampling efficiency at least for elliptical shapes.
+      Lastly hexagons are visually less biased for displaying densities than
+      other regular tesselations. For instance with squares our eyes are drawn
+      to the horizontal and vertical lines of the grid.
+    https://cran.r-project.org/web/packages/hexbin/vignettes/hexagon_binning.pdf
+    """
+
+    _OPTIONS_SPECS = []
+
+
 class FrequencyTable(BaseVisualization):
     """A visualization representing a two-column table with answer counts."""
 
diff --git a/scripts/check_frontend_coverage.py b/scripts/check_frontend_coverage.py
--- a/scripts/check_frontend_coverage.py
+++ b/scripts/check_frontend_coverage.py
@@ -400,7 +400,6 @@
     'state-editor.service.ts',
     'state-graph-visualization.directive.ts',
     'state-hints-editor.directive.ts',
-    'state-improvement-suggestion.service.ts',
     'state-interaction-editor.directive.ts',
     'state-name-editor.directive.ts',
     'state-param-changes-editor.directive.ts',


Test patch:
diff --git a/core/domain/visualization_registry_test.py b/core/domain/visualization_registry_test.py
--- a/core/domain/visualization_registry_test.py
+++ b/core/domain/visualization_registry_test.py
@@ -74,7 +74,7 @@ def test_visualization_class_with_invalid_addressed_info_is_supported(self):
     def test_get_all_visualization_ids(self):
         visualization_ids = (
             visualization_registry.Registry.get_all_visualization_ids())
-        expected_visualizations = ['FrequencyTable', 'BarChart',
+        expected_visualizations = ['FrequencyTable', 'BarChart', 'ClickHexbins',
                                    'EnumeratedFrequencyTable', 'SortedTiles']
 
         self.assertEqual(
@@ -121,7 +121,7 @@ def test_visualization_names(self):
 
         expected_visualizations = ['BaseVisualization', 'FrequencyTable',
                                    'BarChart', 'EnumeratedFrequencyTable',
-                                   'SortedTiles']
+                                   'ClickHexbins', 'SortedTiles']
 
         self.assertEqual(
             sorted(all_visualizations), sorted(expected_visualizations))