Function conv_units() not working inside an ifelse() statement

huangapple go评论101阅读模式
英文:

Function conv_units() not working inside an ifelse() statement

问题

I want to convert geographic coordinates in a table. I have some measurement in decimal degrees (dec_deg) and some in decimal minutes (deg_dec_min). I want to convert those in decimal minutes to decimal degrees. Using the function conv_unit() within a mutate() and ifelse() statement, I get a Warning message and an incorrect value.

Here's a reproducible example:

  1. library(dplyr)
  2. library(measurements)
  3. data_latlon <- tibble(latitude = c(8.726088, -16.365242, -19.888074, '1 40.232', '0 2.308', '2 2.356'),
  4. longitude = c(-83.180764, -62.015502, -40.549983, '75 54.301', '70 56.693', '72 41.143'),
  5. unit = c('dec_deg', 'dec_deg', 'dec_deg', 'deg_dec_min', 'deg_dec_min', 'deg_dec_min'))

Case 1: using ifelse()

  1. data_latlon %>%
  2. mutate(latitude = ifelse(unit=='deg_dec_min',
  3. conv_unit(latitude, from = 'deg_dec_min', to = 'dec_deg'),
  4. latitude))

Case 2: doing it separately

  1. data_latlon %>%
  2. filter(unit=='deg_dec_min') %>%
  3. mutate(latitude = ifelse(unit=='deg_dec_min',
  4. conv_unit(latitude, from = 'deg_dec_min', to = 'dec_deg'),
  5. latitude))

Warning messages:
1: Problem while computing latitude = ifelse(...).
ℹ longer object length is not a multiple of shorter object length
2: Problem while computing latitude = ifelse(...).
ℹ data length is not a multiple of split variable

英文:

I want to convert geographic coordinates in a table. I have some measurement in decimal degrees (dec_deg) and some in decimal minutes (deg_dec_min). I want to convert those in decimal minutes to decimal degrees. Using the function conv_unit() within a mutate() and ifelse() statement, I get a Warning message and an incorrect value.

Here's a reproducible example:

  1. library(dplyr)
  2. library(measurements)
  3. data_latlon &lt;- tibble(latitude = c(8.726088, -16.365242, -19.888074,
  4. &#39;1 40.232&#39;, &#39;0 2.308&#39;, &#39;2 2.356&#39;),
  5. longitude = c(-83.180764, -62.015502, -40.549983,
  6. &#39;75 54.301&#39;, &#39;70 56.693&#39;, &#39;72 41.143&#39;),
  7. unit = c(&#39;dec_deg&#39;, &#39;dec_deg&#39;, &#39;dec_deg&#39;,
  8. &#39;deg_dec_min&#39;, &#39;deg_dec_min&#39;,&#39;deg_dec_min&#39;))

Case 1: using ifelse()

  1. data_latlon %&gt;%
  2. mutate(latitude = ifelse(unit==&#39;deg_dec_min&#39;,
  3. conv_unit(latitude, from = &#39;deg_dec_min&#39;, to = &#39;dec_deg&#39;),
  4. latitude))
  5. # A tibble: 6 &#215; 3
  6. latitude longitude unit
  7. &lt;chr&gt; &lt;chr&gt; &lt;chr&gt;
  8. 1 8.726088 -83.180764 dec_deg
  9. 2 -16.365242 -62.015502 dec_deg
  10. 3 -19.888074 -40.549983 dec_deg
  11. 4 2.34133333333333 75 54.301 deg_dec_min
  12. 5 2.356 70 56.693 deg_dec_min
  13. 6 0 72 41.143 deg_dec_min
  14. Warning messages:
  15. 1: Problem while computing `latitude = ifelse(...)`.
  16. longer object length is not a multiple of shorter object length
  17. 2: Problem while computing `latitude = ifelse(...)`.
  18. data length is not a multiple of split variable

Case 2: doing it separately

  1. data_latlon %&gt;%
  2. filter(unit==&#39;deg_dec_min&#39;) %&gt;%
  3. mutate(latitude = ifelse(unit==&#39;deg_dec_min&#39;,
  4. conv_unit(latitude, from = &#39;deg_dec_min&#39;, to = &#39;dec_deg&#39;),
  5. latitude))
  6. # A tibble: 3 &#215; 3
  7. latitude longitude unit
  8. &lt;chr&gt; &lt;chr&gt; &lt;chr&gt;
  9. 1 1.67053333333333 75 54.301 deg_dec_min
  10. 2 0.0384666666666667 70 56.693 deg_dec_min
  11. 3 2.03926666666667 72 41.143 deg_dec_min

答案1

得分: 1

I believe the issue is measurments::conv_unit is intended to receive numeric vector. However, some of your values in latitude column is character. This would be an issue if you run the entire column simultaneously.

For example, running conv_unit will return warnings and incorrect results:

  1. conv_unit(data_latlon$latitude, from = 'deg_dec_min', to = 'dec_deg')
  2. [1] "8.99884203333333" "-19.9047406666667" "...
  3. Warning messages:
  4. 1: In as.numeric(unlist(strsplit(x_na_free, " "))) * c(3600, 60) :
  5. longer object length is not a multiple of shorter object length
  6. 2: In split.default(as.numeric(unlist(strsplit(x_na_free, " "))) * :
  7. data length is not a multiple of split variable

Solution:

The easiest solutions are to vectorize the function or use rowwise():

  1. conv_unit_vec<-Vectorize(conv_unit)
  2. data_latlon %>%
  3. mutate(latitude = ifelse(unit=='deg_dec_min',
  4. conv_unit_vec(latitude, from = 'deg_dec_min', to = 'dec_deg'),
  5. latitude))
  6. # A tibble: 6 x 3
  7. latitude longitude unit
  8. <chr> <chr> <chr>
  9. 1 8.726088 -83.180764 dec_deg
  10. 2 -16.365242 -62.015502 dec_deg
  11. 3 -19.888074 -40.549983 dec_deg
  12. 4 1.67053333333333 75 54.301 deg_dec_min
  13. 5 0.0384666666666667 70 56.693 deg_dec_min
  14. 6 2.03926666666667 72 41.143 deg_dec_min

or:

  1. data_latlon %>%
  2. rowwise()%>%
  3. mutate(latitude = ifelse(unit=='deg_dec_min',
  4. conv_unit_vec(latitude, from = 'deg_dec_min', to = 'dec_deg'),
  5. latitude)) %>%
  6. ungroup()
  7. # A tibble: 6 x 3
  8. latitude longitude unit
  9. <chr> <chr> <chr>
  10. 1 8.726088 -83.180764 dec_deg
  11. 2 -16.365242 -62.015502 dec_deg
  12. 3 -19.888074 -40.549983 dec_deg
  13. 4 1.67053333333333 75 54.301 deg_dec_min
  14. 5 0.0384666666666667 70 56.693 deg_dec_min
  15. 6 2.03926666666667 72 41.143 deg_dec_min
英文:

I believe the issue is measurments::conv_unit is intended to receive numeric vector. However, some of your values in latitude column is character. This would be an issue if you run the entire column simultaneously.

For example, running conv_unit will return warnings and incorrect results:

  1. conv_unit(data_latlon$latitude, from = &#39;deg_dec_min&#39;, to = &#39;dec_deg&#39;)
  2. [1] &quot;8.99884203333333&quot; &quot;-19.9047406666667&quot; &quot;-40.232&quot;
  3. [4] &quot;2.34133333333333&quot; &quot;2.356&quot; &quot;0&quot;
  4. Warning messages:
  5. 1: In as.numeric(unlist(strsplit(x_na_free, &quot; &quot;))) * c(3600, 60) :
  6. longer object length is not a multiple of shorter object length
  7. 2: In split.default(as.numeric(unlist(strsplit(x_na_free, &quot; &quot;))) * :
  8. data length is not a multiple of split variable

Solution:

The easiest solutions are to vectorize the function or use rowwise():

  1. conv_unit_vec&lt;-Vectorize(conv_unit)
  2. data_latlon %&gt;%
  3. mutate(latitude = ifelse(unit==&#39;deg_dec_min&#39;,
  4. conv_unit_vec(latitude, from = &#39;deg_dec_min&#39;, to = &#39;dec_deg&#39;),
  5. latitude))
  6. # A tibble: 6 x 3
  7. latitude longitude unit
  8. &lt;chr&gt; &lt;chr&gt; &lt;chr&gt;
  9. 1 8.726088 -83.180764 dec_deg
  10. 2 -16.365242 -62.015502 dec_deg
  11. 3 -19.888074 -40.549983 dec_deg
  12. 4 1.67053333333333 75 54.301 deg_dec_min
  13. 5 0.0384666666666667 70 56.693 deg_dec_min
  14. 6 2.03926666666667 72 41.143 deg_dec_min

or:

  1. data_latlon %&gt;%
  2. rowwise()%&gt;%
  3. mutate(latitude = ifelse(unit==&#39;deg_dec_min&#39;,
  4. conv_unit_vec(latitude, from = &#39;deg_dec_min&#39;, to = &#39;dec_deg&#39;),
  5. latitude)) %&gt;%
  6. ungroup()
  7. # A tibble: 6 x 3
  8. latitude longitude unit
  9. &lt;chr&gt; &lt;chr&gt; &lt;chr&gt;
  10. 1 8.726088 -83.180764 dec_deg
  11. 2 -16.365242 -62.015502 dec_deg
  12. 3 -19.888074 -40.549983 dec_deg
  13. 4 1.67053333333333 75 54.301 deg_dec_min
  14. 5 0.0384666666666667 70 56.693 deg_dec_min
  15. 6 2.03926666666667 72 41.143 deg_dec_min

答案2

得分: 1

以下是您要翻译的内容:

Few things to note here:

  • from ?ifelse : "如果test的任何元素为真,仅当yes将被评估,类似于no"; 所以这里都会完全评估yesno

  • conv_unit() 实际上并不检查 x 参数中的值是否合理。从函数源代码中摘录的部分如下:

  1. if (from == "deg_dec_min")
  2. secs = lapply(split(as.numeric(unlist(strsplit(x_na_free,
  3. " "))) * c(3600, 60), f = rep(1:length(x_na_free),
  4. each = 2)), sum)

请注意它使用了 unlist()c(3600, 60)rep(..., each = 2),它依赖于一个假设,即输入向量 x 中的每个元素都会被分割成恰好2个数字,不多不少。
如果你将输入向量 c("8.726088", "-16.365242", "-19.888074", "1 40.232", "0 2.308", "2 2.356") 按照 " " 分割字符串,然后使用 unlist(),你将得到9个数字,而不是12个。这是警告和混乱结果的原因。

除了 rowwise(),你还可以通过使用 split()map_at() 来处理 deg_dec_min 被设置的行,如下所示:

  1. library(measurements)
  2. library(dplyr, warn.conflicts = FALSE)
  3. library(purrr)
  4. # 按 "unit" 列拆分为 tibble 列表,
  5. # 仅对 "deg_dec_min" 部分应用 mutate,
  6. # 再将两个部分合并
  7. data_latlon %>%
  8. split(~ unit) %>%
  9. map_at("deg_dec_min",
  10. \(x) x %>% mutate(across(ends_with("itude"),
  11. \(coord_col) conv_unit(coord_col,
  12. from = 'deg_dec_min',
  13. to = 'dec_deg')))) %>%
  14. list_rbind()
  15. #> # A tibble: 6 × 3
  16. #> latitude longitude unit
  17. #> <chr> <chr> <chr>
  18. #> 1 8.726088 -83.180764 dec_deg
  19. #> 2 -16.365242 -62.015502 dec_deg
  20. #> 3 -19.888074 -40.549983 dec_deg
  21. #> 4 1.67053333333333 75.9050166666667 deg_dec_min
  22. #> 5 0.0384666666666667 70.9448833333333 deg_dec_min
  23. #> 6 2.03926666666667 72.6857166666667 deg_dec_min

输入:

  1. data_latlon <- tibble(latitude = c(8.726088, -16.365242, -19.888074,
  2. '1 40.232', '0 2.308', '2 2.356'),
  3. longitude = c(-83.180764, -62.015502, -40.549983,
  4. '75 54.301', '70 56.693', '72 41.143'),
  5. unit = c('dec_deg', 'dec_deg', 'dec_deg',
  6. 'deg_dec_min', 'deg_dec_min','deg_dec_min'))

创建于2023年05月10日,使用 reprex v2.0.2

英文:

Few things to note here:

  • from ?ifelse : "yes will be evaluated if and only if any element of test is true, and analogously for no"; so both yes and no are fully evaluated here.

  • conv_unit() does not really check if values in x arg make sense.
    Snippet from function source:

  1. if (from == &quot;deg_dec_min&quot;)
  2. secs = lapply(split(as.numeric(unlist(strsplit(x_na_free,
  3. &quot; &quot;))) * c(3600, 60), f = rep(1:length(x_na_free),
  4. each = 2)), sum)

Note how it uses unlist(), c(3600, 60) and rep(..., each = 2), it relies on an assumption that each element in input vector x will be split into exactly 2 numbers, no more no less.
If you take your input vector, c(&quot;8.726088&quot;, &quot;-16.365242&quot;, &quot;-19.888074&quot;, &quot;1 40.232&quot;, &quot;0 2.308&quot;, &quot;2 2.356&quot;), split strings by &quot; &quot;, and unlist, you will get 9 instead of 12 numbers. This is the reason for the warning and messed up result.

Besides rowwise() you could also handle this by e.g. split() and map_at() to only process rows where deg_dec_min is set:

  1. library(measurements)
  2. library(dplyr, warn.conflicts = FALSE)
  3. library(purrr)
  4. # split to list of tibbles by &quot;unit&quot; column,
  5. # apply mutate only on &quot;deg_dec_min&quot; part,
  6. # rbind both parts back together
  7. data_latlon %&gt;%
  8. split(~ unit) %&gt;%
  9. map_at(&quot;deg_dec_min&quot;,
  10. \(x) x %&gt;% mutate(across(ends_with(&quot;itude&quot;),
  11. \(coord_col) conv_unit(coord_col,
  12. from = &#39;deg_dec_min&#39;,
  13. to = &#39;dec_deg&#39;)))) %&gt;%
  14. list_rbind()
  15. #&gt; # A tibble: 6 &#215; 3
  16. #&gt; latitude longitude unit
  17. #&gt; &lt;chr&gt; &lt;chr&gt; &lt;chr&gt;
  18. #&gt; 1 8.726088 -83.180764 dec_deg
  19. #&gt; 2 -16.365242 -62.015502 dec_deg
  20. #&gt; 3 -19.888074 -40.549983 dec_deg
  21. #&gt; 4 1.67053333333333 75.9050166666667 deg_dec_min
  22. #&gt; 5 0.0384666666666667 70.9448833333333 deg_dec_min
  23. #&gt; 6 2.03926666666667 72.6857166666667 deg_dec_min

Input:

  1. data_latlon &lt;- tibble(latitude = c(8.726088, -16.365242, -19.888074,
  2. &#39;1 40.232&#39;, &#39;0 2.308&#39;, &#39;2 2.356&#39;),
  3. longitude = c(-83.180764, -62.015502, -40.549983,
  4. &#39;75 54.301&#39;, &#39;70 56.693&#39;, &#39;72 41.143&#39;),
  5. unit = c(&#39;dec_deg&#39;, &#39;dec_deg&#39;, &#39;dec_deg&#39;,
  6. &#39;deg_dec_min&#39;, &#39;deg_dec_min&#39;,&#39;deg_dec_min&#39;))

<sup>Created on 2023-05-10 with reprex v2.0.2</sup>

huangapple
  • 本文由 发表于 2023年5月10日 20:27:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/76218417.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定