【问题标题】:How to Create Iterative Forumla to calculate Z Score in R?如何创建迭代公式来计算 R 中的 Z 分数?
【发布时间】:2021-01-07 16:01:00
【问题描述】:

我有许多具有以下基本格式的大型数据框,其中最后两行是平均值 (d) 和标准差 (e) - 尽管这些是在其他地方计算的。

  a  b  c
a 4  3  4
b 3  2  6
c 2  1  8
d 3  2  6
e 1  1  2

我想创建一个迭代函数,通过每列 d 和 e 中的平均值和 sd 值将每个原始数据点转换为 z 分数。我想应用的公式是 ((x-mean)/SD)。

结果如下:

   a   b  c
a  1   1  1
b  0   0  0
c -1  -1 -1

我不介意这是添加到末尾、创建为新数据框还是转换数据。

谢谢!

【问题讨论】:

  • 您是指d 还是列(缺失)?
  • 我想使用 d 和 e 行中的值来使用公式 ((x-d/e) 转换列 a - c 中的值。第二个数据中缺少 d ane e框架,因为它们不需要转换

标签: r formula calculation


【解决方案1】:

这是一种方法,请注意,我不使用数据中提供的均值/标准差,而是即时重新计算。 另请注意,通常数据应位于tidy data representation 中,在您的情况下,这意味着 a、b、c 将在列中,然后 mean/sd 将即时计算或位于单独的列中(注意这将重塑数据,此处未显示)。

# your input data
raw_data <- data.frame(
  a = c(4, 3, 2, 3, 1),
  b = c(3, 2, 1, 2, 1),
  c = c(4, 6, 8, 6, 2),
  row.names = c("a", "b", "c", "d", "e")
)
raw_data
#>   a b c
#> a 4 3 4
#> b 3 2 6
#> c 2 1 8
#> d 3 2 6
#> e 1 1 2

# remove the mean/sd values
data <- raw_data[!rownames(raw_data) %in% c("d", "e"), ]
data
#>   a b c
#> a 4 3 4
#> b 3 2 6
#> c 2 1 8

# quick way to recalculate the values
means <- apply(data, 2, mean)
means
#> a b c 
#> 3 2 6
sds   <- apply(data, 2, sd)
sds
#> a b c 
#> 1 1 2

z_scores <- apply(data, 2, function(x) (x - mean(x)) / sd(x))
z_scores
#>    a  b  c
#> a  1  1 -1
#> b  0  0  0
#> c -1 -1  1

reprex package (v0.3.0) 于 2021-01-07 创建

编辑/完整代码

以下代码有点长,但大部分用于将数据转换为正确的(长/整齐)格式。 如果您有任何问题,请随时使用 cmets。

请注意,tidyverse 确实很有帮助,但可能需要一些时间来适应。这里使用的代码大多是dplyr(包含在tidyverse中)。 如果您了解这些功能:%&gt;% (pipe)、group_by()mutate()summarise()pivot_longer/wider(),您就掌握了一切。

library(tidyverse)
# use your original dataset again
raw_data <- data.frame(
  a = c(4, 3, 2, 3, 1),
  b = c(3, 2, 1, 2, 1),
  c = c(4, 6, 8, 6, 2),
  row.names = c("a", "b", "c", "d", "e")
)

### 1) Turn the data into a nicer format

# match-table how to rename the variables
var_match <- c(d = "mean", e = "sd")
# convert the raw data into a nicer format, first we do some minor changes 
# (variable names, etc)
data_mixed <- raw_data %>% 
  # have the rownames as explicit variable
  rownames_to_column("metric") %>%
  # nicer printing etc
  as_tibble() %>% 
  # replace variable names with mean/sd
  mutate(metric = ifelse(metric %in% c("d", "e"),
                      var_match[metric], metric))

data_mixed
#> # A tibble: 5 x 4
#>   metric     a     b     c
#>   <chr>  <dbl> <dbl> <dbl>
#> 1 a          4     3     4
#> 2 b          3     2     6
#> 3 c          2     1     8
#> 4 mean       3     2     6
#> 5 sd         1     1     2

# separate the dataset into two: 
# data holds the values
# data_vars holds the metrics mean and sd
data      <- data_mixed %>% filter(!metric %in% var_match) %>% select(-metric)
data_vars <- data_mixed %>% filter(metric %in% var_match)

data
#> # A tibble: 3 x 3
#>       a     b     c
#>   <dbl> <dbl> <dbl>
#> 1     4     3     4
#> 2     3     2     6
#> 3     2     1     8

data_vars
#> # A tibble: 2 x 4
#>   metric     a     b     c
#>   <chr>  <dbl> <dbl> <dbl>
#> 1 mean       3     2     6
#> 2 sd         1     1     2


# turn the value dataset into its longer form, makes it easier to work with it later
data_long <- data %>% 
  pivot_longer(everything(), names_to = "var", values_to = "val")

data_long
#> # A tibble: 9 x 2
#>   var     val
#>   <chr> <dbl>
#> 1 a         4
#> 2 b         3
#> 3 c         4
#> 4 a         3
#> 5 b         2
#> 6 c         6
#> 7 a         2
#> 8 b         1
#> 9 c         8

# turn the metric dataset into another long form, allowing easy combination in the next step
data_vars2 <- data_vars %>% 
  pivot_longer(-metric, names_to = "var", values_to = "val") %>% 
  pivot_wider(var, names_from = metric, values_from = val)

data_vars2
#> # A tibble: 3 x 3
#>   var    mean    sd
#>   <chr> <dbl> <dbl>
#> 1 a         3     1
#> 2 b         2     1
#> 3 c         6     2

# combine the datasets
data_all <- left_join(data_long, data_vars2, by = "var")

data_all
#> # A tibble: 9 x 4
#>   var     val  mean    sd
#>   <chr> <dbl> <dbl> <dbl>
#> 1 a         4     3     1
#> 2 b         3     2     1
#> 3 c         4     6     2
#> 4 a         3     3     1
#> 5 b         2     2     1
#> 6 c         6     6     2
#> 7 a         2     3     1
#> 8 b         1     2     1
#> 9 c         8     6     2

## 2) calculate the z-score

# now comes the actual number crunchin!
# per variable var (a, b, c) compute the variable val_z as the z-score
data_res <- data_all %>% 
  group_by(var) %>% 
  mutate(val_z = (val - mean) / sd)

data_res
#> # A tibble: 9 x 5
#> # Groups:   var [3]
#>   var     val  mean    sd val_z
#>   <chr> <dbl> <dbl> <dbl> <dbl>
#> 1 a         4     3     1     1
#> 2 b         3     2     1     1
#> 3 c         4     6     2    -1
#> 4 a         3     3     1     0
#> 5 b         2     2     1     0
#> 6 c         6     6     2     0
#> 7 a         2     3     1    -1
#> 8 b         1     2     1    -1
#> 9 c         8     6     2     1


## 3) make the results more readable

# lastly pivot the results to its original form
data_res_wide <- data_res %>% 
  select(var, val_z) %>% 
  group_by(var) %>% 
  mutate(id = 1:n()) %>% # needed for easier identification of values
  pivot_wider(id, names_from = var, values_from = val_z)

data_res_wide
#> # A tibble: 3 x 4
#>      id     a     b     c
#>   <int> <dbl> <dbl> <dbl>
#> 1     1     1     1    -1
#> 2     2     0     0     0
#> 3     3    -1    -1     1

reprex package (v0.3.0) 于 2021-01-07 创建

【讨论】:

  • 请注意,c 列与您预期的输出不同,不确定您是如何到达那里的。
  • 感谢您的帮助,我在制作示例时弄乱了 Excel 中的公式。我已经更新了 c 列。我会试试你的解决方案,谢谢
  • 所以这让我非常接近。但是,我不想重新计算均值和 SD,因为它们实际上来自控制数据集。我也有这些单独的向量,但如果有帮助,可以按照您的建议将它们设为列?
  • 见我上面的更新。如果这对您有所帮助,请考虑为答案投票!
猜你喜欢
  • 1970-01-01
  • 2014-08-18
  • 1970-01-01
  • 1970-01-01
  • 2021-12-07
  • 1970-01-01
  • 1970-01-01
  • 2021-11-17
  • 1970-01-01
相关资源
最近更新 更多