r - 如何将高度测量值转换为统一格式?
问题描述
我有一组数据,包括许多高度测量作为字符变量。有些写成“5ft 7”,有些写成“170cm”,有些写成“1.7m”,有些写成“170”。
我想更改它们,以便它们都显示为没有测量单位的数字变量(例如,只有 170)。
解决方案
数据争吵非常有趣,涉及到一些在边缘情况下的磕磕绊绊和调整:
heights <- c("5ft 7", "170cm", "1.7m", "6' 7", "150", "5' 2\"", "5ft8")
heights
[1] "5ft 7" "170cm" "1.7m" "6' 7" "150" "5' 2\"" "5ft8"
但提供了探索许多工具的机会。以统一的度量,比方说厘米,索引我们所得到的符号:
b4meas <-gsub('[0-9\\. ]', '', heights)
b4meas
[1] "ft" "cm" "m" "'" "" "'\"" "ft"
gsub `[0-9\. ]' 是说给我所有不是数字、点或空格的东西。我们可能希望索引这些不同的情况以进行转换:
which(b4meas== 'ft')
[1] 1 7
which(b4meas== '')
[1] 5
探索数字:
char_num <- gsub('[a-z\']','', heights, perl=TRUE)
char_num
[1] "5 7" "170" "1.7" "6 7" "150" "5 2\"" "58"
> which(nchar(char_num) == 2 & b4meas=='ft')
[1] 7
> which(nchar(char_num) == 3 & b4meas=='ft')
[1] 1
> which(nchar(char_num) == 3 & b4meas=="'")
[1] 4
> which(b4meas=="'\"")
[1] 6
所以我们的异构脚符号,也可以是索引。而我们基于厘米的测量不需要转换:
which(nchar(char_num) == 3 & b4meas=="'" | b4meas == 'cm')
[1] 2 4
所以,让我们看看我们在这里做了什么:
split_char <- strsplit(char_num, ' ')
> split_char
[[1]]
[1] "5" "7"
[[2]]
[1] "170"
[[3]]
[1] "1.7"
[[4]]
[1] "6" "7"
[[5]]
[1] "150"
[[6]]
[1] "5" "2\""
[[7]]
[1] "58"
因此,[[2]] 和 [[5]] 可以不做任何转换,也可以直接写入另一列而无需转换。[[3]] * 100, [[1]] & [[4]] 可以计算,[[6]] 需要进一步清理,[[7]] 需要额外拆分。
sum(as.numeric(split_char[[1]][1])*12 * 2.54, as.numeric(split_char[[1]][2]) * 2.54)
[1] 170.18
# for [[6]]
sum(as.numeric(split_char[[6]][1]) * 12 * 2.54, eval(as.numeric(gsub('\\"', '', split_char[[6]][2])) * 2.54))
[1] 157.48
# either `eval` or `force` can be used to avoid
# Error in gsub( non-numeric argument to binary operator
# for [[7]]
sum(as.numeric(strsplit(split_char[[7]], '')[[1]][1])*12 *2.54, as.numeric(strsplit(split_char[[7]],'')[[1]][2]) * 2.54)
[1] 172.72
好的,我们可以转换,但是等等,我们有一个 data.frame!所以,将使用我们的索引和转换来做到这一点......希望......
> physio_df <- data.frame(heights)
> physio_df[['heights_cm']] <- NA_real_ # add column to convert to
> physio_df
heights heights_cm
1 5ft 7 NA
2 170cm NA
3 1.7m NA
4 6' 7 NA
5 150 NA
6 5' 2" NA
7 5ft8 NA
这是一个奇迹,我们的一些案例仅仅通过使用 data.frame 就得到了简化。但也意味着重新计算以反映这一点很有用b4meas
(因为您已经在 data.frame 中,您不需要这样做)。
# [[5]] just take to numeric
physio_df$heights_cm[which(nchar(physio_df$heights) ==3)] <- physio_df$heights[as.numeric(which(nchar(physio_df$heights) ==3))]
#[[7]]
physio_df$heights_cm[b4meas== 'm'] <- as.numeric(char_num[b4meas == 'm'])* 100
b4meas2 <- gsub('[0-9\\. ]', '', physio_df$heights)
> b4meas2
[1] "ft" "cm" "m" "'" "" "'\"" "ft"
physio_df$heights[[6]]
[1] "5' 2\""
哦,所以它实际上并不是一个奇迹,b4meas
仍然是一个有效的索引。如果您有多个符合标准的案例,则索引的好处是可以解决所有此类案例。
#let's make an index for [[1]] & [[4]] but not [[6]]
one_four_type <- setdiff(which(sapply(split_char, function(x) length(x) == 2)), which(b4meas == "'\""))
# and use in a `for` loop, should `sapply`, data has killed brain
for(i in 1:length(one_four_type)){
+ physio_df$heights_cm[one_four_type[i]] <-
+ sum(as.numeric(split_char[[one_four_type[i]]][1])*12 * 2.54,
+ as.numeric(split_char[[one_four_type[i]]][2]) * 2.54)
+ }
physio_df
heights heights_cm
1 5ft 7 170.18
2 170cm <NA>
3 1.7m 170
4 6' 7 200.66
5 150 150
6 5' 2" <NA>
7 5ft8 <NA>
# physio_df$heights_cm[2]
physio_df$heights_cm[which(b4meas=='cm')] <- as.numeric(char_num[b4meas=='cm'])
# physio_df$heights_cm[6]
> physio_df$heights_cm[which(b4meas == "'\"")] <-
+ sum(as.numeric(split_char[[6]][1]) * 12 * 2.54, eval(as.numeric(gsub('\\"', '', split_char[[6]][2])) * 2.54))
# physio_df$heights_cm[7]
physio_df$heights_cm[7] <- sum(as.numeric(strsplit(split_char[[7]], '')[[1]][1])*12 *2.54, as.numeric(strsplit(split_char[[7]],'')[[1]][2]) * 2.54)
> physio_df
heights heights_cm
1 5ft 7 170.18
2 170cm 170
3 1.7m 170
4 6' 7 200.66
5 150 150
6 5' 2" 157.48
7 5ft8 172.72
推荐阅读
- javascript - 为什么在 reducer 中使用扩展运算符不会给出拆分值?
- multithreading - Form.ShowModal 在 TThread 上不起作用
- python - 线性回归预测接下来的几分钟
- xaml - 设置控件 DataContext,同时将其他属性绑定到父上下文
- javascript - VueJS:将对象作为道具传递给组件-作为null
- php - SQLite 查找范围(3000-4000)之间的缺失数字
- r - 我是否需要遍历 R 中的向量元素来计算相关性?
- csv - F# 合并具有不同列的 CSV 文件
- react-native - 反应导航中的`goBack`不会返回
- c# - C# SQL 数据填充到 Listview 失败