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
Add label properties app #518
Conversation
- copied [object-properties](https://github.com/plotly/canvas-portal/blob/master/apps/object-properties/app.py) app from canvas-portal - changed dash_table calls to work with updated API - used px.imshow to visualize the label data
- if callback inputs were not triggered by user interaction (i.e. when the app is just loaded), just return default values - removed the suppression of callback exceptions
- added dbc to requirements.txt - added dbc stylesheet to app - remove old stylesheets that are no longer needed - moved app and content definition to the top of the file - refactored `data` to `table` to avoid shadowed variable name from `highlight_filter` scope and for clarity - broke layout into components and fit in dbc structure with annotations - added plotly logo - added a README.md file
This PR isn't completely finished yet. For example, the app currently doesn't show very interesting hover information as discussed in #507. I wanted to start the review process at this stage already because there are a couple of changes that I'd like feedback on. |
- my linter didn't catch that...
change to new app name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR! I left a couple of comments. Also, some columns of the table are a bit too wide, it would be possible to change columns headers (euler_number
--> "Euler number") to fit on two lines to have less wide columns. This would make it possible to have a wider column for the figure. What would you think of this?
apps/dash-label-properties/app.py
Outdated
line=dict(width=1), | ||
showscale=False, | ||
colorscale=custom_viridis, | ||
opacity=opacity, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a hovertemplate could be added here to display only the z
value (not x
and y
) and to add for example the mean intensity (passed as customdata
, see for example https://plotly.com/python/hover-text-and-formatting/#adding-other-data-to-the-hover-with-customdata-and-a-hovertemplate)
- use new skimage region property computation function to construct data-table - prevent call to `highlight_row` and `highlight_filter` callbacks at app start.
- pass data-table to image function - match custom hover info with the currently displayed data - also remove margins around figure
apps/dash-label-properties/app.py
Outdated
# Display hover data with precision if data is float | ||
hover_string = [ | ||
f"{col}: %{{customdata[{col_id}]:.3f}}" | ||
if data_table[col].dtype in (np.float,) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not entirely sure why this line works. For example data_table[col].dtype in ("float",)
has the same result which is not encouraging. I think it is somewhat explicit but maybe there is a better way to write this? I don't think I have access to the precision from the pandas object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is basically what np.array_str
does (but it adds these [...]
things around the numbers), so it sort of seems the way to do it. The arrays I think are just numpy arrays underneath. I think it's ok that data_table[col].dtype in "float"
float has the same result, because in numpy you can make an array like np.zeros(N,dtype='float')
or np.zeros(N,dtype=np.float)
and they will have the same type. That it doesn't check that it is one of those two types is a another story involving Python's type checking system in general... but I think we don't have to worry about that (we provided the data, haha).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the pointers @nicholas-esterer! I dug a little further because I didn't understand why np.dtype("float") == "float"
evaluates True
essentially.
The way I understand it now is that 1) numpy datatypes have a hierarchy where np.float64
is a sub-datatype of float
and 2) numpy array's have a __eq__
method that evaluates this hierarchy internally. So you can also do things like np.dtype("float") == "double"
and so on...
Guess I learned something new today!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think to be super explicit here, I'll just go with np.issubdtype
here to avoid any confusion
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe you can get away with just using printf statements, does this do what you want?
>>> '%0.3f' % (1.23,)
'1.230'
>>> '%0.3f' % (1,)
'1.000'
1 is an int but has 0s after it, 1.23 is made to have an extra 0
Sometimes I get the following error message:
(there's more in the traceback but it's not in your app anymore) |
Hhm, I also remember seeing that but I thought I had debugged this and haven't seen it since. Does that usually happen right at the start of the app? I think that might have been taken care off with the
I don't think I understand what this means. Have I removed something that makes the traceback more informative? |
No no, it's just I can't always trigger it, and I didn't look at the problem that long (I had hoped I could show you better where the bug was). Maybe just try putting |
Ah ok, cool, thanks for that. I have definitely also seen this but without remembering the specific fix, I thought I had taken care of it. Would be curious to see what brings it up. |
- more explicit numpy datatype checking for the datatable columns - added 1px margins (l/r) around .row elements to have datatable display fully - added list of unique labels as cache - removed label array Store and update from callback - replaced functionality with list of currently visible labels - some label related refactoring for clarity
bd83cb3
to
afde946
Compare
…label-properties-app
- converted label overlay to png using datashader - added datashader to requirements.txt - additional hidden scatter overlay for hoverinfo - adjusted highlight_row callback to work with the scatter overlay
…dd-label-properties-app
- added customdata with label information
I have encountered a bit of a tricky issue: I have added I don't really understand why this is proving so difficult. After all there is a edit: |
@jonmmease maybe you can help @surchs with the deployment problem coming from datashader's dependencies. @surchs if this is blocking you can probably vendor datashader's module together with the app, keeping only what you need and removing the numba decorator of https://github.com/holoviz/datashader/blob/master/datashader/transfer_functions/__init__.py Before this you can try to also pin numba to an older version explicitly, probably pinning only datashader is not enough. |
I touched base with @surchs offline a bit on Friday. Here are a set of versions that currently work with the old version of pip that is included in the DE buildpack (https://github.com/plotly/dash-world-cell-towers/blob/d02ebf370b6f31196386a1939dc5e3ac3fe71317/requirements.txt). After pip is updated (plotly/heroku-buildpack-python#38), these errors should all go away. |
Thanks @jonmmease for the pointers to the working versions and @emmanuelle for the ping. I will try these out with the next commit! |
- removed hoverinfo for background image - made the content of the hover dependent on the selected columns in the datatable - remove current label cache. Is not necessary because relevant information is available from table data - fixed bug where contour was drawn based on row index rather than label. - updated requirements.txt to pin CircleCI compatible versions (thanks @jonmmease) - removed typecheck in image_with_contour since we supply the PIL objects
I believe that the core functionality is there. Remaining issues:
|
FWIW, span doesn't work with the default shade method ( Code: https://datashader.org/_modules/datashader/transfer_functions.html#shade |
Thank you @surchs I really love the app! The dropdown with the different ways of coloring the objects is very nice. One thing I noticed is that if you want to select an object in the table (the red outline) you need to select a value in the dropdown first or it won't work. I did not investigate why, I can take a look unless it's obvious for you. Also could you please explain
Last thing (before I read the code, I've just tested the deployed app for now): And yes please write one of these great modals you've been treating us with, with a screenshot of the much-improved layout :-). |
@@ -0,0 +1,16 @@ | |||
datashader==0.8.0 | |||
dash-core-components |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought that when you put dash
in the requirements.txt
, or pip install dash, you got dash-core-components, dash-html-components and dash-table together with dash
. Am I missing something? (maybe it's related to the datashader problems?) I'm asking because we've been trying to tell users (on the forum for example) to install dash
only and not to separately install dcc and html because they would run into version incompatibilities.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I may have just copied this over from an existing requirements.txt. I'll make sure we don't need it and then trim the requirements!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked at pipdeptree and all we really need is edit: we also need explicitly dash
, dash_canvas
and dash_bootstrap_components
(now that we got rid of the datashader dependencies)pandas
and gunicorn
. For visibility purposes it might make sense to add skimage
back in, what do you think @emmanuelle
@@ -0,0 +1,9 @@ | |||
/* set the margins of row elements to zero to | |||
override the settings in the dash bootstrap stylesheet */ | |||
.row { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I love it when the CSS stylesheet has a length of 9 lines :-).
For the margins a quick fix would probably to use a different template such as |
They are related to the hidden scatter trace that the hover-info is based on (i.e. if the traces are not added, the figure fills the entire canvas). I assume that the scatter traces add their own x and y range "padding" and therefore extend beyond the base image range. So far I haven't found a good way to keep them from doing that. Maybe I should just manually compute the max/min of the positions and set the ranges to that?
Yeah, I need to explain this :). I was thinking what we could do with the selected_columns feature and so at the moment it controls the information that is shown in the hover-info. That is, you only see the variables from the selected columns (because by default I showed all and that gets a bit busy). I tried to use a datatable tooltip to explain this but I think the version we use here doesn't have that feature yet? The description could either go in the modal or on top of the table inside the card.
Will do :) |
apps/dash-label-properties/app.py
Outdated
|
||
|
||
def image_with_contour( | ||
img, active_labels, data_table, active_columns, color_column, mode="lines" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think in this app mode
is always None so probably you can simplify the code a bit here?
apps/dash-label-properties/app.py
Outdated
|
||
fig = px.imshow(img, binary_string=True, binary_backend="jpg",) | ||
# Overlay the current labels | ||
_, hex_list = zip(*colors.PLOTLY_SCALES["Viridis"]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you wrap this part (the shading) in a helper function? It will be clearer and it's also something we could reuse in other apps so it will be easier to find it back.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we no longer need this code/function in the label-properties app. Do you think we should still wrap it into a helper function and if so, what would be a good place to put it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please remove any code that is not needed any more. In order to keep track of what you did here maybe we could add an example to the imshow page of the plotly doc (on how to pass grayscale image as a binary image string but still use a colormap) but this is quite a hack so maybe we can just remember that this is possible and we'll find the code in the PR if needed?
): | ||
""" | ||
Figure with contour plot of labels superimposed on background image. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this function is quite advanced, would it be possible to give a few explanations in the docstring about the different traces which are used, and the tricks to improve performance?
) | ||
fig.add_scatter( | ||
x=x, | ||
y=y, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is the layout Image above really needed here, or would it be possible just to use the filled scatter traces with suitable colors computed from with datashader? Not very important since it works as it is but I'm just trying to see whether we can reduce the complexity of the function or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think that's a great idea. And I think we don't even need the datashader then anymore because we can simply use the matplotlib cmap object for "viridis" and call the rgb colors for each (normalized) value we want to draw individually. I have committed this and I think it's a lot cleaner and most of the complexity is gone.
Unfortunately I don't know how to integrate the colorbar-scatter into the "color overlay" scatter. So for the moment, we have these two, relatively lengthy scatter traces in there.
OK I finished my review! |
- scatter trace is now opaque and gets colored based on the selected value. px.imshow color overlay is removed - replaced datashader with matplotlib cmap call - removed datashader and dependencies from requirements.txt - trimmed requirements.txt to minimal requirements according to pipdeptree - changed figure template to "simple_white" to hide the added margins, made axes invisible - added initial value to dropdown menu that prevented the region contour from drawing correctly on app start (or before a dropdown value was explicitly set by the user) - increased the line thickness of the region contour to stand out more against the colored scatter trace
incorrectly removed the two dependencies
- added new modal and screenshot / gif - added warning popup if colorscale is deselected (breaks things) - pointed github button to correct repo - added tooltips to datatable to better explain column selection - annotated the image generator function - cleaned up the imports
- screenshot link typo fixed - added stylesheet rule to keep screenshot from overflowing modal
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, thank you @surchs! 💃
Issue for app: #507
App pull request
About
Workflow
elements necessary for successful deployment are in place.
existing gallery app, I've summarized the changes requested in the
appropriate Streambed issue and confirm that they have been applied.
the GitHub repository for the source code in the portal description.
DashR gallery, the app in this PR mimics, as closely as possible,
the style and functionality of the existing app.=
assets/
folder.The pre-review review
I have addressed all of the following questions:
any dead and/or irrelevant code.)
readable and, where it isn't, it has been commented appropriately.)]
lessen the volume of code that needs to be maintained.)