thatsneat
thatsneat

Reputation: 25

Optimize within for loop cannot find function

I've got a function, KozakTaper, that returns the diameter of a tree trunk at a given height (DHT). There's no algebraic way to rearrange the original taper equation to return DHT at a given diameter (4 inches, for my purposes)...enter R! (using 3.4.3 on Windows 10)

My approach was to use a for loop to iterate likely values of DHT (25-100% of total tree height, HT), and then use optimize to choose the one that returns a diameter closest to 4". Too bad I get the error message Error in f(arg, ...) : could not find function "f".

Here's a shortened definition of KozakTaper along with my best attempt so far.

KozakTaper=function(Bark,SPP,DHT,DBH,HT,Planted){
  if(Bark=='ob' & SPP=='AB'){
    a0_tap=1.0693567631
    a1_tap=0.9975021951
    a2_tap=-0.01282775
    b1_tap=0.3921013594
    b2_tap=-1.054622304
    b3_tap=0.7758393514
    b4_tap=4.1034897617
    b5_tap=0.1185960455
    b6_tap=-1.080697381
    b7_tap=0}
  else if(Bark=='ob' & SPP=='RS'){
    a0_tap=0.8758
    a1_tap=0.992
    a2_tap=0.0633
    b1_tap=0.4128
    b2_tap=-0.6877
    b3_tap=0.4413
    b4_tap=1.1818
    b5_tap=0.1131
    b6_tap=-0.4356
    b7_tap=0.1042}
  else{
    a0_tap=1.1263776728
    a1_tap=0.9485083275
    a2_tap=0.0371321602
    b1_tap=0.7662525552
    b2_tap=-0.028147685
    b3_tap=0.2334044323
    b4_tap=4.8569609081
    b5_tap=0.0753180483
    b6_tap=-0.205052535
    b7_tap=0}
  p = 1.3/HT
  z = DHT/HT
  Xi = (1 - z^(1/3))/(1 - p^(1/3))
  Qi = 1 - z^(1/3)
  y = (a0_tap * (DBH^a1_tap) * (HT^a2_tap)) * Xi^(b1_tap * z^4 + b2_tap * (exp(-DBH/HT)) +
                                                    b3_tap * Xi^0.1 + b4_tap * (1/DBH) + b5_tap * HT^Qi + b6_tap * Xi + b7_tap*Planted)
  return(y=round(y,4))}


HT <- .3048*85 #converting from english to metric (sorry, it's forestry)

for (i in c((HT*.25):(HT+1))) {
  d <- KozakTaper(Bark='ob',SPP='RS',DHT=i,DBH=2.54*19,HT=.3048*85,Planted=0)
  frame <- na.omit(d)
  optimize(f=abs(10.16-d), interval=frame, lower=1, upper=90,
           maximum = FALSE,
           tol = .Machine$double.eps^0.25)
}

Eventually I would like this code to iterate through a csv and return i for the best d, which will require some rearranging, but I figured I should make it work for one tree first.
When I print d I get multiple values, so it is iterating through i, but it gets held up at the optimize function. Defining frame was my most recent tactic, because d returns one NaN at the end, but it may not be the best input for interval. I've tried interval=c((HT*.25):(HT+1)), defining KozakTaper within the for loop, and defining f prior to the optimize, but I get the same error. Suggestions for what part I should target (or other approaches) are appreciated!

-KB

Forestry Research Fellow, Appalachian Mountain Club. MS, University of Maine

**Edit with a follow-up question: I'm now trying to run this script for each row of a csv, "Input." The row contains the values for KozakTaper, and I've called them with this:

Input=read.csv...
Input$Opt=0
o <- optimize(f = function(x) abs(10.16 - KozakTaper(Bark='ob',
                                                     SPP='Input$Species',
                                                     DHT=x,
                                                     DBH=(2.54*Input$DBH),
                                                     HT=(.3048*Input$Ht),
                                                     Planted=0)),
              lower=Input$Ht*.25, upper=Input$Ht+1,
              maximum = FALSE,  tol = .Machine$double.eps^0.25)
Input$Opt <- o$minimum
Input$Mht <- Input$Opt/.3048. # converting back to English 

Input$Ht and Input$DBH are numeric; Input$Species is factor. However, I get the error invalid function value in 'optimize'. I get it whether I define "o" or just run optimize. Oddly, when I don't call values from the row but instead use the code from the answer, it tells me object 'HT' not found. I have the awful feeling this is due to some obvious/careless error on my part, but I'm not finding posts about this error with optimize. If you notice what I've done wrong, your explanation will be appreciated!

Upvotes: 1

Views: 160

Answers (1)

emilliman5
emilliman5

Reputation: 5966

I'm not an expert on optimize, but I see three issues: 1) your call to KozakTaper does not iterate through the range you specify in the loop. 2) KozakTaper returns a a single number not a vector. 3) You haven't given optimize a function but an expression.

So what is happening is that you are not giving optimize anything to iterate over.

All you should need is this:

optimize(f = function(x) abs(10.16 - KozakTaper(Bark='ob', 
                                    SPP='RS', 
                                    DHT=x, 
                                    DBH=2.54*19, 
                                    HT=.3048*85, 
                                    Planted=0)),
           lower=HT*.25, upper=HT+1,
           maximum = FALSE,  tol = .Machine$double.eps^0.25)

$minimum
[1] 22.67713 ##Hopefully this is the right answer

$objective
[1] 0

Optimize will now substitute x in from lower to higher, trying to minimize the difference

Upvotes: 1

Related Questions