mrjoh3 / cytoscape

An R HTMLWidget wrapper for Cytoscape.js
GNU General Public License v3.0
6 stars 2 forks source link

Implement cytoscape filtering #5

Open kyleweise opened 6 years ago

kyleweise commented 6 years ago

I think another main action that other users and myself would like to be able to do is filter nodes/edges(i.e. this ), where the usage would be something like

cytoscape(...) %>% nodes() %>% filter('[weight > 50]')

or cytoscape(...) %>% filter_nodes('[weight > 50]')

The first more closely matches the example given by cytoscape.js in the link. Again, my JavaScript is not so good and I'm relatively new to package development in R, but I'm hoping contributing to this repo with aid in both of those areas.

Thanks,

-Kyle

mrjoh3 commented 6 years ago

for this one I think you need to think some more about how it is implemented and what the use case is. filter('[weight > 50]') is simply filtering the data so you could more easily do:

data %>% filter() %>% cytoscape() ...

Now I suspect that you are wanting to interact with the chart after it has rendered. In this case you will need some sort of js UI element to manage the interaction. This could more easily be done in R (likely shiny) with a slider element. The filter() would observe the slider and reapply when necessary. This would be much easier than implementing in JS.

Have a think and let me know.

Matt

kyleweise commented 6 years ago

In the case where you have nice and neat dataframes of nodes and edges to pass to cytoscape(...), I can see using filter(...) beforehand would be easier. In my case, I am building the graph from pre-generated JSON files with cytoscape(json = my_json_string). While I assume doing this kind of filtering/preprocessing with the JSON objects as R lists is not impossible, it's rather difficult. Ergo, this feature request.

My actual use case is a Shiny app where the graph is built off these pre-generated JSON files. Then, I need to have couple groups of checkbox inputs, which basically toggle showing different nodes/edges. So in my server.R of the app, I'm a bit stuck between trying to do something like this, where I handle it all in R:

observeEvent(input$checkboxGroup_1, {
#filter by values in checkboxGroup_1
cytoscape() %>% filter(...)
})

or something like this, where I handle it in JavaScript:

observeEvent(input$checkboxGroup_1, {
message <- as.list(input$checkboxGroup_1)
session$sendCustomMessage("checkboxGroup_1_handler", message)
})
kyleweise commented 6 years ago

Any thoughts on how I might go about this? I'm at a loss for ideas. I'm not even sure the second option is viable because that involves a custom .js file associated with my app, and not the cytoscape.js file of the package. So they would not have access to the same cy = cytoscape(...) object. Let me know your thoughts.

mrjoh3 commented 6 years ago

Sorry Kyle I have not had much spare time lately. I would be using the first option but only because I think the second js implementation will be complicated.

Are you able to import your json and convert it into a dataframe? This would at least allow you to filter the data mite result in r.

On Wed., 25 Jul. 2018, 19:53 kyleweise, notifications@github.com wrote:

Any thoughts on how I might go about this? I'm at a loss for ideas. I'm not even sure the second option is viable because that involves a custom .js file associated with my app, and not the cytoscape.js file of the package. So they would not have access to the same cy = cytoscape(...) object. Let me know your thoughts.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/mrjoh3/cytoscape/issues/5#issuecomment-407700068, or mute the thread https://github.com/notifications/unsubscribe-auth/AB1TAbb-y3YzmsHnDx5IWRph-aVEK7C1ks5uKEAEgaJpZM4VWonn .

kyleweise commented 6 years ago

Yes, I am able to do that. However, using jsonlite::fromJSON on my data results in a super complex dataframe, and loses much of the structure of the JSON that I would then have to reconstruct manually. This is what I was referring to when I said that "filtering/preprocessing with the JSON objects as R lists is not impossible, it's rather difficult" in a previous comment.

Also, have a look at this blog post by Dean Attali, in particular tip 8. I think it might be useful to reconstruct this package a bit to mimic the structure he describes, although I'm not sure exactly how it would work in regards to the many many methods that cytoscape.js has for both the "core" and "collections".

mrjoh3 commented 6 years ago

Hi Kyle,

have a look at the latest commit it is a very basic implementation based on the link you mentioned above. I have not tested that it works only that nothing else broke. Try to incorporate it into a shiny example (it will only work in shiny).

When you define the widget make sure you define the elementId you will need it to apply the filter. You can pass a character filter such as '[weight > 50]'. Let me know if it work and we can start to make it a bit more complex.

Matt

kyleweise commented 6 years ago

Just tried testing this by making some changes to the minimum_shiny app. I added some test weights to the nodes:

 nodes <- data.frame(id = unique(c(df$reporter, df$partner)), stringsAsFactors = FALSE,
                         weight = sample(x = c(1:100), 71))

and added the cy_filter() to the network pipe:

cytoscape(nodes = nodes, edges = edges) %>%
       layout('breadthfirst', directed = TRUE) %>%
       panzoom() %>%
       cy_filter("[weight > 25]")

and although it doesn't throw any errors (in R or JavaScript), the graph does not appear. My first thought is because the functionality for the piping is not set up properly / at all?

mrjoh3 commented 6 years ago

ah of course. this won't work with the pipe as the output is a message and not the entire widget. In shiny you will have to apply the function from inside an observe. The observe can relate to anything, even the loading of the app.

So something like:

cytoscape(nodes = nodes, edges = edges, elementId = 'myWidget')

# call filter here
observeEvent(input$..., {
    cy_filter('myWidget', "[weight > 25]")
})
kyleweise commented 6 years ago

Hey Matt,

see my most recent PR, I've gotten the cy_filter() function to work (somewhat). See if you can't help me figure out the bug I have and if you have any tips for getting my custom function to work.

Thanks,

-Kyle