Closed pjvandehaar closed 7 years ago
In plots rendered with LZ would this be something rendered in tool tips? And if so, would conditional blocks in tool tip markup (#86) be required?
{{EDIT: it looks like https://github.com/statgen/locuszoom/wiki/Scale-Functions#chaining-scale-functions solves my problem with the format in https://github.com/statgen/locuszoom/wiki/Scale-Functions#scale-functions}}
I want:
if (variant.is_LDrefvar) point_shape = 'diamond'
else if(variant.beta&&variant.beta>0 || variant.or&&variant.or>0) point_shape = 'triangle-up'
else if(variant.beta&&variant.beta<0 || variant.or&&variant.or<0) point_shape = 'triangle-down'
else point_shape = 'circle'
I don't think that should require any changes in LZ. I'll do that in the layout if possible, but otherwise I can make a variable effect_direction: [-1,0,1]
in .getData()
.
Looking through scale function docs, it looks like my options are:
d.shape
in .parseResponse()
/.getData()
.point_shape: '|shape'
.
tooltip: '|tooltip'
, which would use the data_layer.tooltip
object, and would mean that tooltip was no longer special, but instead just another attribute and transformation.d.effect_direction
in .parseResponse()
/.getData()
and allow recursive scale functions which use it with much simpler logic.@pjvandehaar I see how the behavior you're looking for here:
if (variant.is_LDrefvar) point_shape = 'diamond'
else if(variant.beta&&variant.beta>0 || variant.or&&variant.or>0) point_shape = 'triangle-up'
else if(variant.beta&&variant.beta<0 || variant.or&&variant.or<0) point_shape = 'triangle-down'
else point_shape = 'circle'
Isn't quite possible with the current implementation of scale functions in layouts. The issue appears to be those more complex conditionals in the middle that operate on more than one field.
I can also see how recursive scale functions would accommodate that, but in my experience recursion is best regarded as a last resort. It certainly has its uses, but it's probably overly complex for emulating simple conditional logic.
I'm thinking more along the lines of expanding the scale function layout directive to allow for a single field or a list of named fields to operate on. Then you could define a scale function (e.g. effect_direction
) that expects both a beta and an OR (gracefully handling undefined values) and returns a direction (-1, 0, or 1). Then the layout directive that defines the point shape would require an array of three values:
if
scale function object mapping the LD ref var boolean to the diamond shape (as currently implemented in the standard association layout)effect_direction
function object mapping the beta and OR fields to the triangles or circleThis is a pretty minor expansion to how layouts parse scalable value directives and would allow for passing arbitrarily many fields to a scale function, which already has the scaffolding to allow for custom definitions. It looks like this would solve your problem without requiring recursion and without pushing more logic into the data source (which, in principle, should do minimal operations on the data itself). But maybe I'm missing something?
Let me know... I'll be developing some this weekend so can implement this and cut a new release by EOD Sunday.
@pjvandehaar I think that the most important plot for the up/down triangles would be the PheWAS plot in order to easily see if the direction of effect is the same for the other traits to which the variant is related.
@pjvh now as of LocusZoom v0.5.6 you should be able to do this. Here's a quick overview...
First, see this test which is patterned off of this very use case. You should be able to define a scale function to essentially cover just the two more complex conditionals like this:
LocusZoom.ScaleFunctions.add("effect_direction", function(parameters, input){
if (typeof input == "undefined"){
return null;
} else if ((input.beta && input.beta > 0) || (input.or && input.or > 0)){
return parameters["+"] || null;
} else if ((input.beta && input.beta < 0) || (input.or && input.or < 0)){
return parameters["-"] || null;
}
return null;
});
Remember that in scale function land null
is the magic value that signals no value was found, so keep stepping through other functions in the array.
The only difference in v0.5.6 is that if you fail to specify a field name in the layout which invokes the scale function then the entire data element is passed in. So with the above custom scale function defined, you should then be able to define your point shape in the layout like this:
...
point_shape: [
{
scale_function: "if",
field: "ld:isrefvar",
parameters: {
field_value: 1,
then: "diamond",
else: null
}
},
{
scale_function: "effect_direction",
parameters:
"+": "triangle-up",
"-": "triangle-down"
},
"circle"
],
...
Note how the else on the first function is null
(instead of "circle") - this will ensure it passed through to the effect direction function.
**
Ultimately it would probably be best to work the effect direction stuff back into the core library, but because you can define and use this as a custom method first it seems to be a better approach to let you work with it in PheWeb and iterate on it until it's performing really well. Then we can take your refined custom stuff and bake it into a future version.
scale_function: "effect_direction"
(which I need to define) instead of pre-computing
In LZ, PheWAS, and Manhattan, use upward/downward triangles to show direction of effect.