Here I go through the process of cleaning the 2025 Google Form entries.

Ready to go 2025 data (As of Cleaning Process for 07-17-2025, doesn’t include ILM)

Import

library(tidyverse)
library(readr)
library(styler)
health_assess_2025 <- read_csv("data/july_16_2025_Butternut Health Assessment Form (Responses).csv")

Isolate Row

# health_assess_2025 <- health_assess_2025 %>% slice(109)
# health_assess_2025$`Any additional notes?`

Removing unnecessary columns for now

health_assess_2025 <- health_assess_2025 %>% select(
  -`Number of the 1st photo taken`,
  -`Number of the last photo taken`,
  -`Producing seed?`,
  -`Roughly how many seeds are on the tree?`,
  -`How many seed are in each bunch (average estimate)?`,
  -`What is the crown class of this individual?`,
  
  # Collections
  -`What did you collect?`,
  -`If VOUCHERS were collected, how many?`,
  -`If LEAVES were collected, how many?`,
  -`If CUTTINGS were collected, how many?`,
  -`If SEEDS were collected, how many?`,
  -`If other collections were made, please describe them here including the number collected.`,
  
  -`How deep are the furrows in the bark?`,
  -`What shade (from light/white to dark) is the tree bark?`,
  
  # Environment
  -`Riparian or upland?`,
  -`Associated tree species within 20 meters.`,
  -`What competition is potentially threatening this tree?`,
  -`If you answered "Other" above, please explain.`,
  -`Does this seedling show signs of damage from any of the following?`,
  -`Does this tree show any signs of any of the following?`,
  
  #Accessory Information
  -Camera,
  -Notes1,
  -Notes2,
  -Notes3,
  -Notes4,
  -Notes5,
  -Notes6,
  -Notes7,
  
  #Editing
  -`Edited after field collection? (Y/N)`,
  -`If edited, what date:`,
  -`If edited, what:`,
)
#colnames(health_assess_2025)

Renaming columns for readability & clarity

Note that this section will not work if the question wording has been changed since July 16th, 2025

basic information

# Plant Height (ft)
health_assess_2025 <- health_assess_2025 %>% rename(plant_height_ft = `Plant Height (in FEET)`)

# DBH
health_assess_2025 <- health_assess_2025 %>% rename(dbh_cm = `DBH (in CENTIMETERS)`)

# has_canker
health_assess_2025 <- health_assess_2025 %>% rename(has_canker = `Visible cankers?`)

# has_callous
health_assess_2025 <- health_assess_2025 %>% rename(has_callous = `If large cankers are present, do you see evidence of callousing, whether currently being calloused over or having previously been calloused over? 

If there are no large cankers present, enter "NA." If there are large cankers present but it is not clear whether to not they are healing over, enter "Maybe."`)

# seedling_y_n
health_assess_2025 <- health_assess_2025 %>% rename(seedling_y_n = `Is this individual a seedling?`)

% observations

# % live canopy
health_assess_2025 <- health_assess_2025 %>% rename(percent_live_canopy = `Percent live canopy (estimate, being sure to only include live branches in assessment)

Note: This is a measure of crown density. In order to estimate this, first envision the amount of canopy there would be if the tree were fully healthy. Butternuts do not typically have a tightly formed canopy even when healthy so be sure to evaluate based on branch presence and location. Then estimate what percent of the envisioned canopy is actually present. This will be your estimate of percent live canopy.`)

# base_epicormics
health_assess_2025 <- health_assess_2025 %>% rename(base_epicormics = `Number of epicormic branches / sprouts from the base`)

# trunk_epicormics
health_assess_2025 <- health_assess_2025 %>% rename(trunk_epicormics = `Number of epicormic branches / sprouts from the trunk`)

# girdled_canker_circum_2025
health_assess_2025 <- health_assess_2025 %>% rename(girdled_canker_circum = `At the part of the trunk that appears most girdled by canker, what portion of the circumference of the trunk is girdled?`)

# trunk_canker_area
health_assess_2025 <- health_assess_2025 %>% rename(trunk_canker_area = `How much area of the trunk below first main branch is infected by canker, measured as a percentage of total trunk with cankers visible (including cankering visible underneath uplifted bark)?`)

# base_canker_area
health_assess_2025 <- health_assess_2025 %>% rename(base_canker_area = `How much area of the base/ root flare is infected by canker, e.g. as a percentage of root flare (up to 10 cm above soil) with cankers visible (including underneath bark)?`)

Densiometer

# densio_north
health_assess_2025 <- health_assess_2025 %>% rename(densio_north = North)

# densio_south
health_assess_2025 <- health_assess_2025 %>% rename(densio_south = South)

# densio_east
health_assess_2025 <- health_assess_2025 %>% rename(densio_east = East)

# densio_west 
health_assess_2025 <- health_assess_2025 %>% rename(densio_west = West)

Categorical (purdue ratings, hybrid characters)

# purdue_severity_canker
health_assess_2025 <- health_assess_2025 %>% rename(purdue_severity_canker = `Assess severity of infection. Focus on the bottom 10 feet of the tree when assessing the number and size of cankers, noting that cankers can be hard to see on old trees with thick bark. CANKERS:`)

# purdue_severity_canopy
health_assess_2025 <- health_assess_2025 %>% rename(purdue_severity_canopy = `Assess severity of infection. CANOPY:`)

# shape_terminal_bud
health_assess_2025 <- health_assess_2025 %>% rename(shape_terminal_bud = `Shape of terminal bud`)

# shape_leaf_scar
health_assess_2025 <- health_assess_2025 %>% rename(shape_leaf_scar = `Shape of leaf scar`)

# shape_lenticels
health_assess_2025 <- health_assess_2025 %>% rename(shape_lenticels = `Shape / length of lenticels`)

# shape_hairs
health_assess_2025 <- health_assess_2025 %>% rename(shape_hairs = `Hairs on the end of the twigs`)
#colnames(health_assess_2025)

Thoughts * do all the canker areas correlate with each other * does one of the canker areas correlate more with percent live canopy loss? * I would hypothesize that girdled circumference area correlates more with

  • if canker doesn’t correlate canopy based on percentages; then do the purdue signs follow same pattern?

Remove individuals from analysis (testing columns, irrelavent sites)

****THIS SHOULD PROBABLY BE OFF OF PLANT NUMBERS INSTEAD****

# Testing columns (#15, 16, 34, 52, 71)
## 15, 16, 71 all explicitly state testing 
## 34 is a BLACK WALNUT
## 52 has unspecific GPS points making me think it is also a testing from when we make it numerical only
health_assess_2025 <- health_assess_2025 %>% slice(c(-15, -16, -34, -52, -71))

# Remove 'Suger River' site from analysis
health_assess_2025 <- filter(
  health_assess_2025,
  
  # Notice that its selecting sites which are NOT sugar river
  !str_equal(
    `Site Number or Initial: JC-W-_______`,
    "Sugar river",
    ignore_case = TRUE
  )
)

Cleaning individual variables

Percent live canopy

a. Removing %s from canopy entries

health_assess_2025$percent_live_canopy <- parse_number(health_assess_2025$percent_live_canopy)

Run this to view the side by side cleaned numerical canopy values (after removing the %s)

# test_canopy <- health_assess_2025 %>% select(percent_live_canopy) %>% mutate(clean_percent_live_canopy = parse_number(health_assess_2025$percent_live_canopy))
# view(test_canopy)

Canker areas (base, trunk, girdled circumference)

a. Removing %s from canker areas

health_assess_2025$base_canker_area <- parse_number(health_assess_2025$base_canker_area)

# Right now, "Less than 10, but more than 0" just reads in as 10
health_assess_2025$trunk_canker_area <- parse_number(health_assess_2025$trunk_canker_area)

# Right now, "Less than 10, but more than 0" just reads in as 10
health_assess_2025$girdled_canker_circum <- parse_number(health_assess_2025$girdled_canker_circum)

Run this to view the side by side cleaned numerical canker areas

# base_canker_test <- health_assess_2025 %>% select(base_canker_area) %>% mutate(cleaned_base_canker = parse_number(health_assess_2025$base_canker_area))
# view(base_canker_test)
# 
# # Right now, "Less than 10, but more than 0" just reads in as 10
# trunk_canker_test <- health_assess_2025 %>% select(trunk_canker_area) %>% mutate(cleaned_trunk_canker = parse_number(health_assess_2025$trunk_canker_area))
# view(trunk_canker_test)
# 
# # Right now, "Less than 10, but more than 0" just reads in as 10
# girdled_canker_test <- health_assess_2025 %>% select(girdled_canker_circum_2025) %>% mutate(cleaned_girdled_canker = parse_number(health_assess_2025$girdled_canker_circum_2025))
# view(girdled_canker_test)

b. If ‘has_canker’ is NO then canker percentages (base/trunk/girdled) are 0.

health_assess_2025 <- health_assess_2025 %>% mutate(base_canker_area = if_else(has_canker == "No", 0, base_canker_area))

health_assess_2025 <- health_assess_2025 %>% mutate(trunk_canker_area = if_else(has_canker == "No", 0, trunk_canker_area))

health_assess_2025$girdled_canker_circum_2025
## Warning: Unknown or uninitialised column: `girdled_canker_circum_2025`.
## NULL
health_assess_2025 <- health_assess_2025 %>% mutate(girdled_canker_circum = if_else(has_canker == "No", 0, girdled_canker_circum))

Run this to view the side by side added 0s for NO

# base_canker_adding_0s_test <- health_assess_2025 %>% select(has_canker, base_canker_area) %>% mutate(cleaned_base_canker = if_else(has_canker == "Yes", base_canker_area, 0))
# view(base_canker_adding_0s_test)
# 
# trunk_canker_adding_0s_test <- health_assess_2025 %>% select(has_canker, trunk_canker_area) %>% mutate(cleaned_trunk_canker = if_else(has_canker == "Yes", trunk_canker_area, 0))
# view(trunk_canker_adding_0s_test)
# 
# girdled_circum_adding_0s_test <- health_assess_2025 %>% select(has_canker, girdled_canker_circum_2025) %>% mutate(cleaned_girdled_circum = if_else(has_canker == "Yes", girdled_canker_circum_2025, 0))
# view(girdled_circum_adding_0s_test)

c. Remove NAs if ‘has_canker’ is NA


DBH remove ‘cm’ text

health_assess_2025 <- health_assess_2025 %>% mutate(
  # Clean up the text for consistency (e.g., remove extra spaces, make lowercase)
  height_str = str_to_lower(str_trim(dbh_cm)),
  
  # Extract the first decimal number, this will always be the cm
    # Note that broadly "\\d+\\.?\\d*" selection nomenclature simily breaks down to: get the "Digits, maybe a dot, maybe more digits"
    # Where the "\\d+" gets all the first whole digits,
    # then the "\\.?" will check whether there is a literal decimal point,
    # if there is then "\\d*" gets all remaining digits
  dbh_cm = as.numeric(str_extract(height_str, "\\d+\\.?\\d*")),

) %>% select(-height_str) # Only keep the new dbh value

Run this to test side-by-side ‘cm’ removal

# test_dbh <- health_assess_2025 %>% select(dbh_cm) %>% mutate(
#   # Clean up the text for consistency (e.g., remove extra spaces, make lowercase)
#   height_str = str_to_lower(str_trim(dbh_cm)),
#   
#   # Extract the first decimal number, this will always be the cm
#     # Note that broadly "\\d+\\.?\\d*" selection nomenclature simily breaks down to: get the "Digits, maybe a dot, maybe more digits"
#     # Where the "\\d+" gets all the first whole digits,
#     # then the "\\.?" will check whether there is a literal decimal point,
#     # if there is then "\\d*" gets all remaining digits
#   dbh_cm_new = as.numeric(str_extract(height_str, "\\d+\\.?\\d*")),
# 
# ) %>% select(-height_str) # Only keep the new dbh value
# view(test_dbh)

Height (Also adults & seedlings)

These are related because I have to verify DBH by having an accurate height

a. Converting all the height texts into feet measurements

1. Conversions assuming unitless entries are feet.

Many of the height entries were first inputted as “1 foot 3 inches”, explicitly writing out the components. Instead, we want just a pure feet measurement in that column. So, I will extract the feet/foot numbers and inches and then convert those accordingly.

health_assess_2025 <- health_assess_2025 %>% mutate(
  # Big picture height cleaning:
  #   * Assume in feet if no units are written into the box
  #   * If units are written in the box: extract them and re-calculate the heigh
  
  # ------------------ Process:
  # Clean up the text for consistency (e.g., remove extra spaces, make lowercase)
  height_str = str_to_lower(str_trim(plant_height_ft)),
  
  # Extract the first decimal number, this will always be the feet
  # Note that broadly "\\d+\\.?\\d*" selection nomenclature simily breaks down to: get the "Digits, maybe a dot, maybe more digits"
  # Where the "\\d+" gets all the first whole digits,
  # then the "\\.?" will check whether there is a literal decimal point,
  # if there is then "\\d*" gets all remaining digits
  feet_str = str_extract(height_str, "\\d+\\.?\\d*\\s*(ft)"),
  feet = as.numeric(str_extract(feet_str, "\\d+\\.?\\d*")),
  
  # Get the string of inches which will be based on either the presence of "inches" or "in",
  # e.g., "7 inches" or "7in"
  inches_str = str_extract(height_str, "\\d+\\.?\\d*\\s*(inches|in)"),
  # Extract the decimal from the isolated inches string, like the feet
  inches = as.numeric(str_extract(inches_str, "\\d+\\.?\\d*")),
  
  # Convert using the numerical values
  # Where 'coalesce' will use a 0 if feet/inches is an NA value
  calculated_from_text_feet = (coalesce(feet, 0)) + (coalesce(inches, 0) / 12.0),
  
  # Seeing if the entry has additional text in it like "ft" or "inches"
  contains_text = str_detect(height_str, "ft") |
    str_detect(height_str, "inches") | str_detect(height_str, "in"),
  
  # Assume the plant_height_ft is the string as a number if the entry doesn't have text.
  plant_height_ft = if_else(
    !contains_text,
    as.numeric(height_str),
    calculated_from_text_feet
  )
) %>% select(
  -height_str,
  -feet_str,
  -feet,
  -inches_str,
  -inches,
  -calculated_from_text_feet,
  -contains_text
)
## Warning: There was 1 warning in `mutate()`.
## ℹ In argument: `plant_height_ft = if_else(!contains_text,
##   as.numeric(height_str), calculated_from_text_feet)`.
## Caused by warning in `if_else()`:
## ! NAs introduced by coercion

Run this to see the full break down off the calculation and extraction

# test_height_cleaning <- health_assess_2025 %>% select(plant_height_ft) %>% mutate(
#   # Big picture height cleaning:
#   #   * Assume in feet if no units are written into the box
#   #   * If units are written in the box: extract them and re-calculate the heigh
#   
#   # ------------------ Process: 
#   # Clean up the text for consistency (e.g., remove extra spaces, make lowercase)
#   height_str = str_to_lower(str_trim(plant_height_ft)),
# 
#   # Extract the first decimal number, this will always be the feet
#     # Note that broadly "\\d+\\.?\\d*" selection nomenclature simily breaks down to: get the "Digits, maybe a dot, maybe more digits"
#     # Where the "\\d+" gets all the first whole digits,
#     # then the "\\.?" will check whether there is a literal decimal point,
#     # if there is then "\\d*" gets all remaining digits
#   feet_str = str_extract(height_str, "\\d+\\.?\\d*\\s*(ft)"),
#   feet = as.numeric(str_extract(feet_str, "\\d+\\.?\\d*")),
# 
#   # Get the string of inches which will be based on either the presence of "inches" or "in",
#     # e.g., "7 inches" or "7in"
#   inches_str = str_extract(height_str, "\\d+\\.?\\d*\\s*(inches|in)"),
#   # Extract the decimal from the isolated inches string, like the feet
#   inches = as.numeric(str_extract(inches_str, "\\d+\\.?\\d*")),
# 
#   # Convert using the numerical values
#     # Where 'coalesce' will use a 0 if feet/inches is an NA value
#   calculated_from_text_feet = (coalesce(feet, 0)) + (coalesce(inches, 0) / 12.0),
#   
#   # Seeing if the entry has additional text in it like "ft" or "inches"
#   contains_text = str_detect(height_str, "ft") | str_detect(height_str, "inches") | str_detect(height_str, "in"),
#   
#   # Assume the plant_height_ft is the string as a number if the entry doesn't have text.
#   plant_height_ft_cleaned = if_else(!contains_text, as.numeric(height_str), calculated_from_text_feet)
#   
# ) %>% select(plant_height_ft, plant_height_ft_cleaned)
# 
# view(test_height_cleaning)

2. Verifying unitless entries were actually in feet.

For the first case of translating height entries from text to numbers there were explicit units like “1 foot 3 inches.” However, others lacked explicit units and were listed as “67.”

In our case, it would be easy to have accidently inputted inches because we were using a ruler for the seedlings. So, I went through and verified that the unitless entries were in fact in feet, and not inches, by accident.

To do this, I viewed the new calculated heights alongside, dbh, densiometer and addditional notes:

# Viewing subset of health_assess_2025 data
view_height <- health_assess_2025 %>% select(`Plant Number (e.g. 4th tree assessed will be 4)`, dbh_cm, plant_height_ft, densio_north, `Any additional notes?`)

# Ordering with highest height at the top
view_height <- view_height[order(view_height$plant_height_ft, decreasing = T), ]

library(knitr)
kable(slice(view_height, 1:20), , caption = "Comparison of heights to verify unitless entries were in feet")
Comparison of heights to verify unitless entries were in feet
Plant Number (e.g. 4th tree assessed will be 4) dbh_cm plant_height_ft densio_north Any additional notes?
24 39.5 69.00 NA Bad visibility of the canopy
25 49.5 66.28 NA NA
16 48.5 65.00 NA fairly vigorous overall; has grapevine; some canopy dieback but stem looks healthy, most cankers are in canopy. in the SW there is an ash right next to it potentially shading it in that direction.
27 53.2 62.34 NA NA
SH29 NA 62.00 66 open Some black streaks from leaves rubbing on cage, some terminal die back on both of 2 terminal branches that it is recovering from
21 20.5 62.00 NA NA
23 37.0 61.00 NA Vine growing up the tree. Tree is wrapped by them.
18 36.5 59.71 NA NA
26 76.5 53.00 NA nice shade tree, open grown, short open grown
017 46.0 50.20 NA several dead butternut nearby, one right behind it to the west and one to the south
191 17.0 50.00 NA NA
19 40.1 48.50 NA NA
20 43.2 45.60 NA NA
SH12 30.5 45.00 NA Sean roughly estimated to be 50 years old. We would have given it a 4 based on canopy but it does not have epicormics.
211 19.0 45.00 NA Dead; Dale estimated that the main trunk died 10 years ago.
*Epicormic may be graft-able.
22 31.2 41.99 NA many small healed cankers ~ several dozen, on edge of field by seedlings, 2 dead butternuts to the west
27 28.0 41.00 NA Some sites FGCA visits, tree species are part of the competition.
1 34.5 40.00 NA Too tall to characterize hybrid characters
2 32.0 40.00 NA DEAD tree. Canopy class is based on structure, not live canopy. Callous is also based on whether cankers look like they have ever been calloused.
SH3 23.0 40.00 NA We did this assessment based on dead adult tree. Epicormic health characterization will be entered as its own form.

Notice that the plant with the apparent greatest height (SH29), also has a densiometer measure. We only took densiometer measures for seedlings. This leads me to believe that SH29’s height was actually written inches, and thus was not 62 feet tall. I then confirmed that SH29 was a seedling with the photos.

Thus, I seperately overrided SH29’s height to be re-evaluated as inches:

# Identified mistake with SH29's height. 
# Based on photo evidence, the input of 62 feet into the datasheet must have been a mistake and treating the 62 as inches makes sense for that individual.

health_assess_2025 <- health_assess_2025 %>% mutate(plant_height_ft = recode(health_assess_2025$plant_height_ft, `62` = (62.0 / 12)))


# In the future, if I need to recode a lot of values I found this useful tidbit:
# df <- mutate(df, height = case_when(
#   height < 2.5 ~ height * 100,
#   height < 100 ~ height + 100
# )

b. Ensure that all trees >4 feet tall have a DBH and are identified as an adult

  • We were not very consistent with our definitions of adult versus seedlings
  • It is not true that all trees >4 feet have a DBH and are identified as an adult
  • My guess is that densiometer was the real way we decided whether it was an adult or not
  • We measured the dbh as 1 cm for a few of the seedlings / later began the basal diameter measurements, as well.
# test_adults_have_dbh <- health_assess_2025 %>% select(`Plant Number (e.g. 4th tree assessed will be 4)`, dbh_cm, plant_height_ft, seedling_y_n) %>% mutate(
#   has_dbh = (!is.na(dbh_cm)),
# )
# 
# view(test_adults_have_dbh)

c. Does densiometer predict seedling or adult?

  • Only one individual identified as a seedling lacked a densiometer reading
# test_adults <- health_assess_2025 %>% select(`Plant Number (e.g. 4th tree assessed will be 4)`, densio_north, seedling_y_n) %>% mutate(
#   is_seedling_with_densio = ((!is.na(densio_north)) & (seedling_y_n == "Yes"))
# )
# 
# view(test_adults)

d. If it has a DBH then it is an adult

This was Emma and I’s agreed upon definition as of July 17th, 2025

Purdue canker severity ratings (Canker & Canopy)

Removes additional text description

health_assess_2025 <- health_assess_2025 %>% mutate(
  purdue_severity_canker = recode(
    health_assess_2025$purdue_severity_canker,
    "1. Fewer than 3 active cankers that are all smaller than 2-3 inches in length or diameter OR fewer than 3 inactive cankers." = "1",
    "2. More than 3 active cankers, OR 2-5 shallow (with no dead tissue) healed over with cracks less than 7 inches long." = "2",
"3. More than 5 active OR inactive cankers cracked through the bark to the tissue below which have healed over, but you still see the level of damage." = "3",
"4. Cankers occur all over the 10-foot area, with deep cracks and both active and inactive cankers." = "4",
"5. Tree almost dead, mostly inactive cankers with deep cracks to dead tissue." = "5"
  )
)

Densiometer

Retrieving the numbers from the rest of the columns

Verify parsing is running correct

# North
test_densio_cleaning <- health_assess_2025 %>% 
  select(densio_north) %>% 
  mutate(densio_north_cleaned = parse_number(health_assess_2025$densio_north))
## Warning: There was 1 warning in `mutate()`.
## ℹ In argument: `densio_north_cleaned =
##   parse_number(health_assess_2025$densio_north)`.
## Caused by warning:
## ! 7 parsing failures.
## row col expected                                                          actual
##   3  -- a number W - 32.  E - 76. S - 10. N - 32. 39% empty or 61% canopy cover.
##   4  -- a number S - 1. E - 56. N - 53. W - 54.  43% open or 57% canopy cover.  
##   6  -- a number W - 33. E - 12. N - 68. S - 2. 30% open, 70% canopy cover      
##   8  -- a number E - 8. S - 5. W - 10. N - 95 30.7% filled, 69.3% canopy cover  
##   9  -- a number E - 7. S - 6. W - 1. N - 5. 19.76% open, 80.24% canopy cover   
## ... ... ........ ...............................................................
## See problems(...) for more details.
# Ordering with highest height at the top
test_densio_cleaning <- test_densio_cleaning[order(test_densio_cleaning$densio_north_cleaned, decreasing = T), ]

# # East
# test_densio_cleaning <- health_assess_2025 %>%
#   select(densio_east) %>%
#   mutate(densio_east_cleaned = parse_number(health_assess_2025$densio_east))
# 
# # South
# test_densio_cleaning <- health_assess_2025 %>%
#   select(densio_south) %>%
#   mutate(densio_south_cleaned = parse_number(health_assess_2025$densio_south))
# 
# # West
# test_densio_cleaning <- health_assess_2025 %>%
#   select(densio_west) %>%
#   mutate(densio_west_cleaned = parse_number(health_assess_2025$densio_west))


library(knitr)
kable(slice(test_densio_cleaning, 1:50), , caption = "Comparison of cleaned densiometer readings to originals")
Comparison of cleaned densiometer readings to originals
densio_north densio_north_cleaned
96 96
96 96
96 96
96 96
96 96
96 96
96 96
96 96
96 96
96 96
96 96
96 96
96 96
96 96
95 95
95 95
94 94
93 93
92 92
91 91
89 89
88 88
87 87
86 86
85 85
83 83
83 83
82 open 82
80 open 80
80 80
78 open 78
77 open 77
72 72
66 open 66
64 64
62 62
61 61
60 60
60 open 60
53 53
50 open 50
49 49
49 49
48 48
45 45
40 40
37 37
36 36
32 32
23 23

Commit parsing

#densio
health_assess_2025$densio_east <- parse_number(health_assess_2025$densio_east)
health_assess_2025$densio_north <- parse_number(health_assess_2025$densio_north)
## Warning: 7 parsing failures.
## row col expected                                                          actual
##   3  -- a number W - 32.  E - 76. S - 10. N - 32. 39% empty or 61% canopy cover.
##   4  -- a number S - 1. E - 56. N - 53. W - 54.  43% open or 57% canopy cover.  
##   6  -- a number W - 33. E - 12. N - 68. S - 2. 30% open, 70% canopy cover      
##   8  -- a number E - 8. S - 5. W - 10. N - 95 30.7% filled, 69.3% canopy cover  
##   9  -- a number E - 7. S - 6. W - 1. N - 5. 19.76% open, 80.24% canopy cover   
## ... ... ........ ...............................................................
## See problems(...) for more details.
health_assess_2025$densio_south <- parse_number(health_assess_2025$densio_south)
health_assess_2025$densio_west <- parse_number(health_assess_2025$densio_west)
## Warning: 1 parsing failure.
## row col expected                                                                                                                         actual
##  22  -- a number Densiometer measures not relevant because seedling was overtaken by honeysuckle. Honeysuckle was cut away before assessing. ng

View results

library(knitr)
kable(slice(health_assess_2025, 1:20), caption = "Cleaned and processed 2025 health assessment data")
Cleaned and processed 2025 health assessment data
Timestamp Email Address Date Site Number or Initial: JC-W-_______ Plant Number (e.g. 4th tree assessed will be 4) GPS location NORTH GPS Location WEST Slope (degree) Aspect (N, NE, E, etc) plant_height_ft dbh_cm percent_live_canopy base_epicormics trunk_epicormics has_canker has_callous trunk_canker_area girdled_canker_circum base_canker_area purdue_severity_canker purdue_severity_canopy shape_terminal_bud shape_leaf_scar shape_lenticels shape_hairs densio_north densio_east densio_south densio_west Any additional notes? seedling_y_n Plant Initials (for example, SH or EL)
6/5/2025 10:07:29 6/5/2025 SH 70 41.892576 -88.226243 0 NA 6.500000 1.20 100 0 0 Yes No 10 50 30 3 1. No apparent crown dieback. 0 0 0 0 NA NA NA NA No samples were collected. Crown class is NA. Terminal bud was NA. Appears to have walnut mite damage - or something like that - on about one quarter of the leaves. Surrounded by bramble. Within 10 meters of the ditch at the north edge of the site. Also within 10 meters of the power lines. Now needs a cage. NA NA
6/5/2025 10:33:48 6/5/2025 SH 67 41.892507 -88.226261 0 NA 6.000000 1.20 100 1 - dead 0 Yes Yes 10 40 30 1 1. No apparent crown dieback. 0 0 1 0 NA NA NA NA It is kind of bulky - thick twigs and big bud scars. Nothing collected. Na for crown class. NA for bark phenotype. Additional sapling/ possible epicormic coming up near the base of the tree. One dead one appears to be epicormic and was entered as such. The one that may be a seedling was not entered as an epicormic because it was not clear if it was a seedling or epicormic. Canopy cover is NA. Surrounded by brambles. Within 10 meters of ditch and also power lines. NA NA
6/5/2025 11:21:22 6/5/2025 SH 71 41.892598 -88.226597 30 N 3.375000 NA 100 0 0 Yes Yes 10 10 10 1 1. No apparent crown dieback. 0 0 1 0 NA NA NA NA NA for DBH, too small. Very small canker cracks. 2 resprouts from 1 cm base that was once mowed. NA for terminal bud. Blistery lenticels. NA NA
6/5/2025 11:47:58 6/5/2025 SH 72 41.892562 -88.22709 0 NA 6.000000 1.00 100 0 0 Yes Yes 5 40 10 1 1. No apparent crown dieback. 0 0 1 0 NA NA NA NA within 10 meters of ditch and power lines. will need caging. mite damage. vine was wrapping around it which was removed. leaf scars a little bit hulky. NA for bark phenotype, 1.5 for overall canker severity. NA NA
6/5/2025 13:28:31 6/5/2025 SH 18 41.892325 -88.228259 0 NA 35.000000 37.30 90 0 0 Yes Yes 30 40 40 4 2. Some but limited crown dieback. 0 1 0 0 NA NA NA NA We’re not entirely sure that the major trunk damage is canker damage. It could be some kind of mechanical - or lightning - damage. Come back to check on the seeds. Is right next to a recently mowed area. Didn’t collect anything. No terminal bud. NA for canopy cover. NA NA
6/5/2025 14:00:52 6/5/2025 SH 17 41.892486 -88.228256 0 NA 1.500000 NA 100 1 very small but dead 0 Yes No 5 10 0 1 1. No apparent crown dieback. 0 0 0 0 NA NA NA NA 1 totally dead twig that looks like it was probably an epicormic. Mite damage. NA for DBH. NA for crown class. NA for terminal bud, bark phenotype, and canker callous. NA NA
6/5/2025 14:34:52 6/5/2025 SH 69 41.892567 -88.227675 0 NA 11.000000 4.00 100 0 0 Yes Yes 25 30 60 2 1. No apparent crown dieback. 0 0 2 0 NA NA NA NA NA for bark phenotype, terminal bud, and densiometer NA NA
6/5/2025 14:54:16 6/5/2025 SH 44 41.892517 -88.227645 0 NA 4.000000 NA 100 0 0 No NA 0 0 0 NA NA 0 0 1 0 NA NA NA NA NA on bark phenotype, terminal bud, crown class, blistery lenticels, mite damage. This is 2 stems coming up where it looks to have been previously mowed. NA NA
6/6/2025 9:37:32 6/6/2025 SH 43 41.892816 -88.227357 0 NA 2.500000 NA 100 0 0 Yes No 20 20 0 1 1. No apparent crown dieback. 0 0 0 0 NA NA NA NA Buckthorn, honeysuckle, sumac, bur oak, and unknown. Has been mowed in the past. NA for DBH, bark phenotype, or crown class. No terminal bud. NA for canker callous. NA NA
6/6/2025 10:28:15 6/6/2025 SH 48 41.89219 -88.227309 0 NA 16.000000 6.00 100 0 0 Yes Yes 30 50 70 3 1. No apparent crown dieback. 0 1 0 0 NA NA NA NA Major damage to one side of the trunk, unsure if from canker, deer rub, or other. Nearby tree(not butternut) has similar damage. Had smaller but significant second trunk that is dead now. NA to terminal bud and densiometer. NA NA
6/6/2025 10:48:21 6/6/2025 SH 45 41.892277 -88.22726 0 NA 6.000000 1.50 100 0 0 Yes Yes 20 50 100 3 1. No apparent crown dieback. 0 0 0 0 NA NA NA NA There is 1 dead possible epicormic coming from the base. Cut stump about an inch in diameter, NA for terminal bud and densiometer. NA NA
6/6/2025 11:02:25 6/6/2025 SH46 46 41.892266 -88.22705 0 NA 9.500000 2.50 100 0 0 Yes Yes 20 40 0 2 1. No apparent crown dieback. 0 0 0 0 NA NA NA NA NA for bark phenotype and densiometer. NA NA
6/6/2025 11:24:42 6/6/2025 SH 47 41.892363 -88.22715 0 NA 4.500000 NA 100 0 0 Yes No 5 5 0 1 1. No apparent crown dieback. 0 0 1 0 NA NA NA NA Dead branch coming up from the trunk about 18 inches off the ground. Kind of a hulky tree. NA for bark phenotype, crown class, and canker calloused. NA NA
6/6/2025 11:41:35 6/6/2025 SH 74 41.892328 -88.227094 0 NA 6.500000 1.50 100 0 0 Yes Yes 15 40 30 2 1. No apparent crown dieback. 0 0 1 0 NA NA NA NA 1 dead branch coming up from the base. Lenticels are blistery and a little bigger than usual. NA for bark phenotype, terminal bud, and densiometer. NA NA
6/12/2025 9:27:26 6/12/2025 SH 37 41.892676 -88.224128 0 0 7.500000 3.00 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA No NA
6/12/2025 9:30:48 6/12/2025 WCP SH37 Na Na NA NA 7.500000 3.00 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA No NA
6/12/2025 9:46:40 6/12/2025 WCP SH37 Na Na 0 0 7.500000 3.00 90 0 0 Yes No 5 20 30 1.5 2. Some but limited crown dieback. NA 0 0 0 NA NA NA NA Surrounded by some thorny species No NA
6/12/2025 10:00:13 6/12/2025 WCP SH75 41.892603 -88.224133 0 NA 2.750000 NA 100 0 0 Yes Maybe 5 50 40 2 1. No apparent crown dieback. NA 0 0 0 64 48 16 63 NA Yes NA
6/12/2025 10:20:26 6/12/2025 WCP SH28 41.892572 -88.224932 0 NA 7.000000 2.75 40 0 0 Yes No 25 65 80 4 2.5 NA 0 1 0 NA NA NA NA Also shrubs nearby as competition No NA
6/12/2025 10:34:42 6/12/2025 WCP SH34 41.892565 -88.225166 0 NA 2.708333 NA 100 0 0 Yes NA 15 40 0 1 1. No apparent crown dieback. NA 0 0 0 5 6 12 10 Some black streaks on the petioles which might be canker Yes NA