The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.
This short vignette provides coding examples on how to use the
functions provided by the aquacultuR package to calculate
feed conversion metrics and nutrient use
efficiency metrics.
library(aquacultuR)
library(magrittr)
library(dplyr)
library(tidyr)
library(lubridate)
oldopts <- options()
options(digits = 3)
The data we will assess in order to demonstrate the calculation of feed conversion metrics originates from one out of two experiments during with Atlantic salmon (Salmo salar) has been exposed to different levels of dissolved oxygen saturation. The original data was published by Liland et al. (2024) in form of a typical Excel workbook and can be found together with additional information in the dedicated publication. The respective data has been tidied by converting the double-row into single-row column names. More information on tidy data can be found for instance in Wickham (2014). In addition, proximate and amino acid compositions have been converted from percentages to mass fractions in gram per gram.
The original dataset provides some information on the feed they used, such as the proximate composition and essential amino acid profile.
feedcomp
#> # A tibble: 1 × 25
#> diet dry_matter crude_protein crude_lipids ash gross_energy phosphorus
#> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 Protec, S… 0.93 0.474 0.22 0.08 22.1 0.012
#> # ℹ 18 more variables: arginine <dbl>, histidine <dbl>, isoleucine <dbl>,
#> # leucine <dbl>, lysine <dbl>, threonine <dbl>, tryptophan <dbl>,
#> # valine <dbl>, methionine <dbl>, cysteine <dbl>, phenylalanine <dbl>,
#> # tyrosine <dbl>, aspartic_acid <dbl>, glutamic_acid <dbl>, alanine <dbl>,
#> # glycine <dbl>, proline <dbl>, serine <dbl>
We can notice that the dry matter content has not been scaled to 1 (=100%). The other data is thus provided on wet weight basis, which will be important later.
After removing the non-numeric diet column, we will convert the data into long format for improved readability.
feedcomp %>%
select(-diet) %>%
pivot_longer(
everything(),
names_to = "parameter",
values_to = "value"
)
#> # A tibble: 24 × 2
#> parameter value
#> <chr> <dbl>
#> 1 dry_matter 0.93
#> 2 crude_protein 0.474
#> 3 crude_lipids 0.22
#> 4 ash 0.08
#> 5 gross_energy 22.1
#> 6 phosphorus 0.012
#> 7 arginine 0.033
#> 8 histidine 0.0099
#> 9 isoleucine 0.0174
#> 10 leucine 0.032
#> # ℹ 14 more rows
The unit of the data is gram per gram. It is highly recommended to
use a package such as units to ensure that there are no
mistakes occurring with unit conversion.
To calculate the feed conversion and nutrient use efficiency, we need
the biomass of the fish stock at the beginning and the end of the growth
phase. This data can be found in the samplings dataset. As the
data is not in the right shape, we need to make some adjustments. First
of all, we will add an additional column (timepoint) as
identifier for the fish bodyweight at the beginning and the end. In the
next step, we will calculate the arithmetic mean of the weights for each
or our timepoints of interest because at the beginning of the growth
period the weight was determined in bulk, while fish were weighed
individually at the end of the growth period. Lastly, we will convert
the data into wide format.
df <- samplings %>%
mutate(
timepoint = case_when(
date == ymd("2023-03-16") ~ "bw_beginning",
date == ymd("2023-04-14") ~ "bw_end",
.default = NA
)) %>%
group_by(tank, timepoint, sample_type) %>%
summarise(mean_weight = mean(fish_weight)) %>%
select(-sample_type, -starts_with("sd")) %>%
pivot_wider(
names_from = timepoint,
values_from = mean_weight
) %>%
print()
#> `summarise()` has grouped output by 'tank', 'timepoint'. You can override using
#> the `.groups` argument.
#> # A tibble: 9 × 3
#> # Groups: tank [9]
#> tank bw_beginning bw_end
#> <fct> <dbl> <dbl>
#> 1 T1 295 385.
#> 2 T10 313 385.
#> 3 T11 305 369.
#> 4 T2 315 394.
#> 5 T3 328 370.
#> 6 T6 332 398.
#> 7 T7 310 402.
#> 8 T8 309 417.
#> 9 T9 305 378.
Additional data we require is that related to the feed intake throughout the growth phase. This can be found in the feed_intake dataset.
head(feed_intake)
#> # A tibble: 6 × 4
#> date tank daily_feed_intake cumulative_feed_intake
#> <date> <fct> <dbl> <dbl>
#> 1 2023-03-16 T1 2.80 2.80
#> 2 2023-03-16 T2 2.51 2.51
#> 3 2023-03-16 T3 2.90 2.90
#> 4 2023-03-16 T6 2.57 2.57
#> 5 2023-03-16 T7 2.80 2.80
#> 6 2023-03-16 T8 2.81 2.81
To calculate nutrient use efficiency, we also need some data on the bodycomposition at the beginning and the end of the growth period, which can be found in the bodycomp dataset.
head(bodycomp)
#> # A tibble: 6 × 14
#> date treatment tank dm water ash energy fat protein ca k
#> <date> <fct> <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 2023-03-16 Initial init… 0.343 0.657 0.0188 0.0946 0.148 0.19 4200 3700
#> 2 2023-03-16 Initial init… 0.339 0.661 0.0183 0.0928 0.136 0.18 3500 3600
#> 3 2023-03-16 Initial init… 0.334 0.666 0.016 0.0916 0.134 0.18 4900 3700
#> 4 2023-04-14 DO50 T1 0.325 0.675 0.0179 0.0873 0.12 0.18 4500 3700
#> 5 2023-04-14 DO50 T2 0.332 0.668 0.0158 0.0877 0.123 0.19 4600 3700
#> 6 2023-04-14 DO50 T3 0.338 0.662 0.0188 0.0923 0.133 0.19 4000 3900
#> # ℹ 3 more variables: mg <dbl>, na <dbl>, phosphorus <dbl>
To assess feed conversion, we need data on the feed intake throughout the experiment. The feed_intake dataset already comes with the cumulative feed intake pre-calculated, which means we only need to retain the ultimate value. We then join the bodyweight at the beginning and the end of the growth period with the feed intake dataset.
df <- feed_intake %>%
filter(date == max(date)) %>%
select(tank, cumulative_feed_intake) %>%
right_join(df, join_by(tank)) %>%
print()
#> # A tibble: 9 × 4
#> tank cumulative_feed_intake bw_beginning bw_end
#> <fct> <dbl> <dbl> <dbl>
#> 1 T1 64.9 295 385.
#> 2 T2 67.3 315 394.
#> 3 T3 69.2 328 370.
#> 4 T6 74.2 332 398.
#> 5 T7 69.4 310 402.
#> 6 T8 73.8 309 417.
#> 7 T9 76.5 305 378.
#> 8 T10 78.6 313 385.
#> 9 T11 67.9 305 369.
It is eventually possible to calculate the Feed Conversion
Ratio (FCR) with the fcr() function and the
Feed Conversion Efficiency (FCE) with the
fce() function. Note that we account for the dry matter
content of the feed, which is not 100%.
feed_conversion <- df %>%
group_by(tank) %>%
summarise(
feed_conversion_ratio = fcr(
ibw = bw_beginning,
fbw = bw_end,
feed = cumulative_feed_intake,
dm = 0.93
),
feed_conversion_efficiency = fce(
ibw = bw_beginning,
fbw = bw_end,
feed = cumulative_feed_intake,
dm = 0.93
)
) %>%
print()
#> # A tibble: 9 × 3
#> tank feed_conversion_ratio feed_conversion_efficiency
#> <fct> <dbl> <dbl>
#> 1 T1 0.667 1.50
#> 2 T10 1.02 0.979
#> 3 T11 0.989 1.01
#> 4 T2 0.793 1.26
#> 5 T3 1.55 0.646
#> 6 T6 1.05 0.950
#> 7 T7 0.701 1.43
#> 8 T8 0.633 1.58
#> 9 T9 0.974 1.03
The FCR indicates the quantity of feed required to gain a unit of bodyweight. The FCE, meanwhile, is the inverse of the FCR and thus describes the bodyweight gain per mass unit of feed. Please note that the specific FCR or FCE (economic, biological, etc.) depend on whether the feed quantity is corrected for feed refusal or other kinds of feed losses and whether both the feed quantity and biomass are corrected for their respective dry matter contents.
To look deeper into the Nutrient Use Efficiency, we
are interested in calculating metrics such as the Nutrient
Efficiency Ratio (NER). This can be achieved with the
ner() function. For this purpose, we need to add the feed
composition to the dataset as well. As an example, we will focus on the
crude protein and calculate the Protein Efficiency Ratio. Note
that the ner() function requires the feed composition data
to be provided in gram per gram, which corresponds to possible values
within the interval [0,1].
df %>%
bind_cols(feedcomp %>%
select(dry_matter, crude_protein)) %>%
mutate(
protein_efficiency_ratio = ner(ibw = bw_beginning,
fbw = bw_end,
fi = cumulative_feed_intake,
nut_f = crude_protein,
dm = dry_matter)
) %>%
relocate(tank, protein_efficiency_ratio) # change column order
#> # A tibble: 9 × 7
#> tank protein_efficiency_ratio cumulative_feed_intake bw_beginning bw_end
#> <fct> <dbl> <dbl> <dbl> <dbl>
#> 1 T1 3.16 64.9 295 385.
#> 2 T2 2.66 67.3 315 394.
#> 3 T3 1.36 69.2 328 370.
#> 4 T6 2.00 74.2 332 398.
#> 5 T7 3.01 69.4 310 402.
#> 6 T8 3.33 73.8 309 417.
#> 7 T9 2.17 76.5 305 378.
#> 8 T10 2.06 78.6 313 385.
#> 9 T11 2.13 67.9 305 369.
#> # ℹ 2 more variables: dry_matter <dbl>, crude_protein <dbl>
An additional metric we might be interested in is the actual
Nutrient Retention (NR), which can be calculated with
the nr() function. The Nutrient Retention indicates how
much of a specific nutrient provided has actually been retained in the
tissue of the fed organism. For this purpose, we require data on the
tissue composition.
summary(bodycomp)
#> date treatment tank dm
#> Min. :2023-03-16 DO50 :3 initial sample:3 Min. :0.302
#> 1st Qu.:2023-04-06 DO60 :3 T1 :1 1st Qu.:0.325
#> Median :2023-04-14 DO95 :3 T10 :1 Median :0.331
#> Mean :2023-04-06 Initial:3 T11 :1 Mean :0.328
#> 3rd Qu.:2023-04-14 T2 :1 3rd Qu.:0.335
#> Max. :2023-04-14 T3 :1 Max. :0.343
#> (Other) :4
#> water ash energy fat
#> Min. :0.657 Min. :0.0158 Min. :0.0793 Min. :0.099
#> 1st Qu.:0.665 1st Qu.:0.0167 1st Qu.:0.0868 1st Qu.:0.120
#> Median :0.669 Median :0.0185 Median :0.0877 Median :0.124
#> Mean :0.672 Mean :0.0180 Mean :0.0880 Mean :0.125
#> 3rd Qu.:0.675 3rd Qu.:0.0190 3rd Qu.:0.0918 3rd Qu.:0.133
#> Max. :0.698 Max. :0.0202 Max. :0.0946 Max. :0.148
#>
#> protein ca k mg na
#> Min. :0.180 Min. :3500 Min. :3600 Min. :330 Min. :760
#> 1st Qu.:0.180 1st Qu.:3950 1st Qu.:3700 1st Qu.:340 1st Qu.:778
#> Median :0.185 Median :4250 Median :3750 Median :350 Median :805
#> Mean :0.185 Mean :4525 Mean :3767 Mean :351 Mean :803
#> 3rd Qu.:0.190 3rd Qu.:4675 3rd Qu.:3825 3rd Qu.:360 3rd Qu.:822
#> Max. :0.190 Max. :6800 Max. :3900 Max. :390 Max. :860
#>
#> phosphorus
#> Min. :3700
#> 1st Qu.:4175
#> Median :4200
#> Mean :4358
#> 3rd Qu.:4400
#> Max. :5500
#>
In an initial step, we specify the start and end date again.
df2 <- bodycomp %>%
mutate(
timepoint = case_when(
date == ymd("2023-03-16") ~ "beginning",
date == ymd("2023-04-14") ~ "end",
.default = NA
)
)
Now we calculate the initial tissue protein content based on the three samples that were taken to determine the body composition and save the result in a new R object.
initial <- df2 %>%
group_by(timepoint) %>%
summarise(tissue_protein_start = mean(protein)) %>%
ungroup() %>%
filter(timepoint == "beginning") %>%
select(tissue_protein_start) %>% print()
#> # A tibble: 1 × 1
#> tissue_protein_start
#> <dbl>
#> 1 0.183
Eventually, we can remove the initial timepoint and add the data together with the feed intake, weight gain, and feed composition data.
nutrient_retention <- df2 %>%
filter(timepoint != "beginning") %>%
rename(tissue_protein = "protein") %>%
select(-timepoint) %>%
bind_cols(initial) %>%
right_join(df, join_by(tank)) %>%
bind_cols(feedcomp %>% select(dry_matter, crude_protein))
After joining all necessary data, we can calculate the NR.
nutrient_retention %>%
mutate(
protein_retention = nr(ibw = bw_beginning,
fbw = bw_end,
ibn = tissue_protein_start,
fbn = tissue_protein,
fi = cumulative_feed_intake,
nut_f = crude_protein
)
) %>%
select(treatment, protein_retention)
#> Inputs differ in length.
#> # A tibble: 9 × 2
#> treatment protein_retention
#> <fct> <dbl>
#> 1 DO50 0.497
#> 2 DO50 0.536
#> 3 DO50 0.307
#> 4 DO60 0.304
#> 5 DO60 0.594
#> 6 DO60 0.647
#> 7 DO95 0.335
#> 8 DO95 0.421
#> 9 DO95 0.326
sessionInfo()
#> R version 4.5.2 (2025-10-31)
#> Platform: x86_64-apple-darwin20
#> Running under: macOS Sequoia 15.7.3
#>
#> Matrix products: default
#> BLAS: /Library/Frameworks/R.framework/Versions/4.5-x86_64/Resources/lib/libRblas.0.dylib
#> LAPACK: /Library/Frameworks/R.framework/Versions/4.5-x86_64/Resources/lib/libRlapack.dylib; LAPACK version 3.12.1
#>
#> locale:
#> [1] C/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
#>
#> time zone: Europe/Prague
#> tzcode source: internal
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] lubridate_1.9.4 tidyr_1.3.2 dplyr_1.1.4 magrittr_2.0.4
#> [5] aquacultuR_1.1.1
#>
#> loaded via a namespace (and not attached):
#> [1] vctrs_0.7.0 cli_3.6.5 knitr_1.51 rlang_1.1.7
#> [5] xfun_0.56 otel_0.2.0 purrr_1.2.1 generics_0.1.4
#> [9] jsonlite_2.0.0 glue_1.8.0 htmltools_0.5.9 sass_0.4.10
#> [13] rmarkdown_2.30 evaluate_1.0.5 jquerylib_0.1.4 tibble_3.3.1
#> [17] fastmap_1.2.0 yaml_2.3.12 lifecycle_1.0.5 compiler_4.5.2
#> [21] timechange_0.3.0 pkgconfig_2.0.3 rstudioapi_0.18.0 digest_0.6.39
#> [25] R6_2.6.1 utf8_1.2.6 tidyselect_1.2.1 pillar_1.11.1
#> [29] bslib_0.9.0 withr_3.0.2 tools_4.5.2 cachem_1.1.0
options(oldopts)
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.