Michael, John and Bert,

Thank you all very much for your help. I think I have gained a lot of understanding and am ready to write better code. I will study "The R Inferno". I appreciate it.

- Alex


________________________________
 From: R. Michael Weylandt <michael.weylandt@gmail.com>
To: Alex Zhang <alex.zhang@ymail.com> 
Cc: John Fox <jfox@mcmaster.ca>; "r-help@r-project.org" <r-help@r-project.org> 
Sent: Tuesday, December 27, 2011 6:59 PM
Subject: Re: [R] sapply Call Returning " the condition has length > 1" Error

Your puzzle comes from a collision of two somewhat subtle facts that
i) sapply() is a wrapper for lapply(), not apply() and ii)
data.frame()s are secretly columnwise lists. Because of this, sapply =
lapply takes each list element = data.frame column and passes it to
the column individually. Compare this behavior to

lapply(1:4, function(x)  max(x^2))

which converts its not-list input (=c(1,2,3,4)) into a list (=
list(1,2,3,4)) before processing. This is different than
lapply(list(1:4), function(x) max(x^2))

If you want to work element wise, you can work with apply() rather
than sapply(), Vectorize(), or just a plain-ol' for loop.

Does this help?

Michael

PS -- You should nag your collaborator about making a non-vectorized
function. If you got the warning message that started this all off,
there's likely a bug in his code of the if+else vs ifelse variety. If
you've never seen a document called "the R inferno" before, Google it,
and take a look through: it's full of all sorts of helpful
intermediate level tips and these sorts of subtleties are well
documented.

On Tue, Dec 27, 2011 at 4:03 PM, Alex Zhang <alex.zhang@ymail.com> wrote:
> John,
>
> Thank you for your comment.
>
> There is no secret. But the actual function I need to call is rather irrelevant. However don't take it as the "abs" function. If you like to know, it is a function that converts 4 kinds of old ids from several old database tables into a new id in a new database. Again, I don't think providing such detail is better than saying MyDummyFunc maps a number into a number but doesn't work with vectors.
>
> All I need to do, is to call DummyFunc for every element in a column of a data.frame and returns the resulted vector. But, I cannot change DummyFunc. Correct me if I am wrong: this is rather common in a group collaboration enviroment. Person A may be responsible for writing a function and person B who needs to use that function cannot or better not change it.
>
> Obviously, I could write a loop. Michael in a previous post suggested using vectorize which works perfectly. As a newbie of R, I would wish to learn more ways to achieve my goal (sorry, it automatically involves "how" not just "what" ;). Is there a way using a "*apply" function to do it where * stands for any function.
>
> Thanks a lot!
>
>
> ________________________________
>  From: John Fox <jfox@mcmaster.ca>
> To: 'Alex Zhang' <alex.zhang@ymail.com>
> Cc: r-help@r-project.org
> Sent: Tuesday, December 27, 2011 4:06 PM
> Subject: RE: [R] sapply Call Returning " the condition has length > 1" Error
>
> Dear Alex,
>
>> -----Original Message-----
>> From: Alex Zhang [mailto:alex.zhang@ymail.com]
>> Sent: December-27-11 3:34 PM
>> To: John Fox
>> Cc: r-help@r-project.org
>> Subject: Re: [R] sapply Call Returning " the condition has length > 1"
>> Error
>>
>> John,
>>
>> Thanks for the pointers.
>>
>> The DummyFunc is just a made-up example. The true function I need to
>> use is more complicated and would be distractive to include.
>
> You'll probably get a better answer if you don't keep what you want to do a
> secret.
>
>>
>> Do you mean that sapply would take columns in the input data.frame and
>> feed them into "FUN" as "whole" vectors? That explains the behavior.
>
> Yes. As I said, a data frame is a list of columns, so FUN is called with
> each column as its argument.
>
>> Is there an "*apply" function that will fee elements of the input
>> data.frame into "FUN" instead of whole columns? Thanks.
>
> I'm afraid that I don't know what you mean. Do you want to deal with the
> columns of the data frame separately (in general, they need not all be of
> the same class), and within each column, apply a function separately to each
> element? You could nest calls to lapply() or sapply(), as in
>
> sapply(D, function(DD) sapply(DD, abs))
>
> assuming, of course, that D is an entirely numeric data frame. But in this
> case,
>
> abs(as.matrix(D))
>
> would be more sensible, and using sapply() like this isn't necessarily
> better than a loop. Again, not knowing what you want to do makes it hard to
> suggest a solution.
>
> Best,
> John
>
>>
>> ________________________________
>>
>> From: John Fox <jfox@mcmaster.ca>
>> To: 'Alex Zhang' <alex.zhang@ymail.com>
>> Cc: r-help@r-project.org
>> Sent: Tuesday, December 27, 2011 3:10 PM
>> Subject: RE: [R] sapply Call Returning " the condition has length > 1"
>> Error
>>
>> Dear Alex,
>>
>> > -----Original Message-----
>> > From: r-help-bounces@r-project.org [mailto:r-help-bounces@r-
>> > project.org] On Behalf Of Alex Zhang
>> > Sent: December-27-11 2:14 PM
>> > To: r-help@r-project.org
>> > Subject: [R] sapply Call Returning " the condition has length > 1"
>> > Error
>> >
>> > Dear all,
>> >
>> > Happy new year!
>> >
>> > I have a question re using sapply. Below is a dummy example that
>> would
>> > replicate the error I saw.
>> >
>> > ##Code Starts here
>> > DummyFunc <- function(x) {
>> >
>> > if (x > 0) {
>> > return (x)
>> > } else
>> > {
>> > return (-x)
>> > }
>> >
>> > }
>> >
>> > Y = data.frame(val = c(-3:7))
>> > sapply(Y, FUN = DummyFunc)
>> > ##Code ends here
>> >
>> > When I run it, I got:
>> >      val
>> >  [1,]  3
>> >  [2,]  2
>> >  [3,]  1
>> >  [4,]  0
>> >  [5,]  -1
>> >  [6,]  -2
>> >  [7,]  -3
>> >  [8,]  -4
>> >  [9,]  -5
>> > [10,]  -6
>> > [11,]  -7
>> > Warning message:
>> > In if (x > 0) { :
>> >  the condition has length > 1 and only the first element will be
>> used
>> >
>> > The result is different from what I would expect plus there is such
>> an
>> > error message.
>>
>> This is a warning, not really an error message. A data frame is
>> essentially a list of variables (columns), and sapply() applies its
>> FUN argument to each list element, that is, each variable -- the one
>> variable val in your case.
>> That produces a warning because val > 0 is a vector of 11 elements,
>> and the first comparison, 3 > 0, which is TRUE, controls the result.
>>
>> >
>> > I guess if the DummyFunc I provided is compatible with vectors, the
>> > problem would go away. But let's suppose I cannot change DummyFunc.
>> Is
>> > there still a way to use sapply or alike without actually writing a
>> > loop? Thanks.
>>
>> Well, you could just use
>>
>> > abs(Y$val)
>> [1] 3 2 1 0 1 2 3 4 5 6 7
>>
>> but I suppose that you didn't really want to write your own version of
>> the absolute-value function as something more than an exercise.
>>
>> An alternative is
>>
>> > with(Y, ifelse(val > 0, val, -val))
>> [1] 3 2 1 0 1 2 3 4 5 6 7
>>
>> I hope this helps,
>> John
>>
>> --------------------------------
>> John Fox
>> Senator William McMaster
>>   Professor of Social Statistics
>> Department of Sociology
>> McMaster University
>> Hamilton, Ontario, Canada
>> http://socserv.mcmaster.ca/jfox
>>
>>
>>
>> >
>> > - Alex
>> >     [[alternative HTML version deleted]]
>>
>>
>>
>>
>        [[alternative HTML version deleted]]
>
>
> ______________________________________________
> R-help@r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-help
> PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
> and provide commented, minimal, self-contained, reproducible code.
>
	[[alternative HTML version deleted]]

