scale_area() and negative values

30 views
Skip to first unread message

JiHO

unread,
May 20, 2009, 5:43:50 PM5/20/09
to ggplot2
Hi again,

scale_area() does not deal with negative values:

qplot(x=1:2, 1:2, size=c(-1,1))
qplot(x=1:2, 1:2, size=c(-1,1)) + scale_area()

It has always been the case I think, but I just used to default to
size=radius rather than size=area. However, currently I would really
need a the enhanced perceptual mapping that scale_area provides.

I understand that this is because scale_area is really just scale size
with a sqrt transformation of the scale. I think I found where in the
code this is specified but I have no clue how to change it (proto is
still impenetrable for me). Would that be something easy enough to
change Hadley? Ideally it should behave as scale_size with respect to
negative values, i.e.
small points for negative values, bigger points for larger, positive
values by default
big points for very negative or very positive values and small points
for 0 when 'to' is specificed

Thanks in advance for your help.

JiHO
---
http://jo.irisson.free.fr/

baptiste auguie

unread,
May 21, 2009, 5:37:33 AM5/21/09
to JiHO, ggplot2
Just an idea --- why not shift the data by adding an offset (absolute
value of the minimum negative value), then use the sqrt()
transformation? I seem to have some problems with the labels though,
somehow they seem to require an inverse transform which cannot be
uniquely defined here ...


sqrtshift <- function(x=-3:2){
if(any(range(x) < 0 )) x <- x - min(x)
sqrt(x)
}

# sqrtshift()
# sqrtshift(1:3)

TransSqrtshift <- Trans$new("sqrtshift",
function(x) {if(any(range(x) < 0 )) x <- x - min(x) ; sqrt(x)})

ScaleSqrtshift <- proto(ScaleContinuous,
desc = "Position scale, shifted and square root transformed",
tr_default = Trans$find("sqrtshift"),
objname = "sqrtshift",
doc=FALSE,
examples=function(.) {}
)

ScaleArea <- proto(
ScaleSize,
desc = "Scale area",
objname = "area",
icon = function(.) ScaleSize$icon(),
details = "",
doc = FALSE,
new = function(., name=NULL, limits=NULL, breaks=NULL, labels=NULL,
to = c(1, 6)) {
.super$new(., name = name, limits = limits, breaks = breaks,
labels = labels, trans="sqrtshift", to = to)
}

)

qplot(x=1:2, 1:2, size=c(-1,1))
qplot(x=1:2, 1:2, size=c(-1,1)) + scale_area()



baptiste
--

_____________________________

Baptiste Auguié

School of Physics
University of Exeter
Stocker Road,
Exeter, Devon,
EX4 4QL, UK

Phone: +44 1392 264187

http://newton.ex.ac.uk/research/emag
______________________________

JiHO

unread,
May 21, 2009, 11:31:15 AM5/21/09
to baptiste auguie, ggplot2
On 2009-May-21 , at 05:37 , baptiste auguie wrote:

> Just an idea --- why not shift the data by adding an offset (absolute
> value of the minimum negative value), then use the sqrt()
> transformation? I seem to have some problems with the labels though,
> somehow they seem to require an inverse transform which cannot be
> uniquely defined here ...

Well, this is actually what I did (but in a much less elegant way): I
shifted my data above 0. But then, as you notice, the labels in the
legen are wrong, so I hade to recompute them and specify them manually.

Apparently a new transform takes 4 arguments: the name, the
transformation function, the inverse transformation function and the
function to generate labels:

new <- function(., name, f="force", inverse="force",
labels="force", ...) {
.$proto(
objname = name,
.transform = f,
.transform_inverse = inverse,
.transform_labels = labels,
...
)
}

I tried to use attributes to be able to store the minimum and hence
define an inverse transformation:

TransSqrtshift <- Trans$new("sqrtshift",
function(x) {
if(any(range(x) < 0 )) x <- x - min(x);

attr(x,"shiftMin") <- min(x);
sqrt(x)
},
function(x) {
x <- x^2;
return(x + attr(x, "shiftMin"))
},
function(x) {
x <- x^2;
return(x + attr(x, "shiftMin"))
}
)

ScaleSqrtshift <- proto(ScaleContinuous,
desc = "Position scale, shifted and square root transformed",
tr_default = Trans$find("sqrtshift"),
objname = "sqrtshift",
doc=FALSE,
examples=function(.) {}
)

ScaleArea <- proto(
ScaleSize,
desc = "Scale area",
objname = "area",
icon = function(.) ScaleSize$icon(),
details = "",
doc = FALSE,
new = function(., name=NULL, limits=NULL, breaks=NULL, labels=NULL,
to = c(1, 6)) {
.super$new(., name = name, limits = limits, breaks = breaks,
labels = labels, trans="sqrtshift", to = to)
}
)

qplot(x=1:2, y=1:2, size=c(-1,1))
qplot(x=1:2, y=1:2, size=c(-1,1)) + scale_area()
ggplot(data.frame(x=1:2, y=1:2, size=c(-1,1))) +
geom_point(aes(x=x,y=y,size=size)) + scale_area()

But this currently does not work:
Error in l[numeric] <- f(unlist(l[numeric])) :
replacement has length zero

I can trace it to the labels call:
> traceback()
17: get("labels", env = scales[[1]], inherits = TRUE)(scales[[1]],
...)
16: scales[[1]]$labels()
15: FUN("\"size\""[[1L]], ...)
14: lapply(vars, function(var) {
scales <- .$get_scales_by_name(var)
if (length(scales) == 0)
...

but then I don't really know where to go.

hadley wickham

unread,
Jun 4, 2009, 5:33:06 PM6/4/09
to JiHO, ggplot2

I'd probably do it like this:

df <- data.frame(x=1:2, y = 1:2, val=c(-1,5))

qplot(x, y, size = abs(val), colour = factor(sign(val)), data = df) +
scale_area()

That way positive and negative values of the same magnitude have the
same size but different colours.

Hadley

--
http://had.co.nz/

Reply all
Reply to author
Forward
0 new messages