For循环:计算两个数据帧之间的匹配和唯一元素,并将函数应用于计数

时间:2017-12-08 18:08:40

标签: r for-loop

我想进行一个非常复杂的循环。我有多个区域,每个区域在我的实际数据框中有数百个图。我想按区域进行子集化,然后在子集上绘制和预先形成各种函数,以最终计算欠共享物种的不相似性。我将在序言中说每一行代表一种互动。

我的示例df

 set.seed(540)
 df<- data.frame(region= c(rep(1, 16), rep(2,8)), 
            plot= c(rep("A",5), rep("B",9), rep("C", 2), rep("D", 6),rep("E", 2)), 
            plantsp= sample(1:24,24, replace= TRUE), 
            lepsp= sample(1:24,24,replace= TRUE), 
            psitsp= sample(1:24,24,replace= TRUE))
 df[] <- lapply(df, as.character)
 df$plantsp<-paste('plantsp', df$plantsp, sep='_')
 df$lepsp<-paste('lepsp', df$lepsp, sep='_')
 df$psitsp<-paste('psitsp', df$psitsp, sep='_')
 df$paste1<- paste(df$plantsp, df$lepsp, sep='_')
 df$paste2<- paste(df$lepsp, df$psitsp, sep='_')
 df$paste3<- paste(df$plantsp,df$lepsp, df$psitsp)

步骤1:按区域子集df。例如:

region_sub <- split(df, df$region)

步骤2:按图绘制子集df。例如:

plot_sub <- split(region_sub[[1]], region_sub[[1]][[2]])

步骤3:我们将从绘图子集上方的步骤调用每个子集(每个列表组件)。在这个例子中,我将使用第一个子集(region1,plotA)作为所有后续输出的示例。我将调用此region1,plotA subset plot_sub1。我想将plot_sub1与原始df进行比较,以生成三个df子集。我们会将这些df_sub1df_sub2df_sub3称为。首先,df_sub1plantsplepspplot_sub1df列中的条目之间的匹配组成。将删除包含任何唯一条目的行,以及plantsp匹配但不匹配lepsp的行,反之亦然。 df_sub1的示例:

df_sub1<- df[c(1,2,3,4,5,22),c(1:4,6)] 

请注意,只有具有共享物种的行仍然存在。此外,只有具有共同物种的那些行也会保持相互作用。此外,我删除了不必要的列(例如psitsppaste2paste3),以引起您对此步骤结果的注意。不需要为代码删除这些列。

步骤4:对lepsppsitsp列重复步骤3以生成df_sub2。例如:

df_sub2<- df[1:5,c(1:2,4,5,7)] 

步骤5:对plantsplepsppsitsp列重复步骤3以生成df_sub3。例如:

df_sub3<- df[1:5,c(1:5,8)] 

步骤6:既然已经制作了所有子集,我想计算paste1plot_sub1(= 5)中df_sub1列中的匹配元素。例: 这将存储在向量match中。结果将相应地存储在匹配或唯一向量中。示例:

match<- length(intersect(df_sub1$paste1,  plot_sub[[1]]$paste1))
match

我还想计算独特的元素(= 1)。这将存储在向量unique中。对于plot_sub1df_sub2以及plot_sub1df_sub3,将重复此操作。我不知道如何计算两个df中的唯一元素,所以我不能提供示例代码。

 unique<- 1

注意:plot_sub中的匹配只需要在df_sub重复交互或匹配的情况下计算一次。这需要考虑是否存在匹配,而不是丰度。

总结这个子集,两个向量将是:

match<- c( length(intersect(df_sub1$paste1,  plot_sub[[1]]$paste1)),  
length(intersect(df_sub2$paste2,  plot_sub[[1]]$paste2)),  
length(intersect(df_sub3$paste3,  plot_sub[[1]]$paste3))

match

unique<-c(1,0,0)

然后将为每个向量合计总和。例如:

sum_match<- 15
sum_unique<- 1

Step7:最后,这些值将输入到函数中:        ((a + b)/((2 * a + b)/ 2) - 1)其中a = sum_match且b = sum_unique。        然后将该值输入结果向量res_vec

步骤8:对每个绘图子集迭代此过程(步骤3-7)。

实际上,这将计算绘图交互和相应的metaweb(所有可能的交互)之间共享交互的不相似性。这是(Poisot等人2012)对三重营养相互作用的修改。

这很可悲,但是为了启动for循环,我有:

res_vec<- NA

for (i in 1:length(unique(df$region)))
  {
      for (j in 1:length(unique(df$plot)))
     {

我非常感谢任何人愿意帮助我实现循环中的论点。这就是让我变得棘手的地方。

1 个答案:

答案 0 :(得分:1)

Thans @Gregor,你已经在评论中做了所有澄清!

以下是我使用tidyverse

的解决方案

代码+说明

## Load packages
library(tidyverse)

## Nest data
new_df <- df %>% 
  group_by(region, plot) %>% 
  nest(.key = plot_sub) 

new_df

# A tibble: 5 x 3
#     region   plot         plot_sub
#      <dbl> <fctr>           <list>
#   1      1      A <tibble [5 x 3]>
#   2      1      B <tibble [9 x 3]>
#   3      1      C <tibble [2 x 3]>
#   4      2      D <tibble [6 x 3]>
#   5      2      E <tibble [2 x 3]>

plot_sub包含与您问题中具有相同名称的列表相同的数据。将此列视为数据框列表。

我知道写一个函数来创建df_sub&#39。这使我们的代码更加干净,并避免不必要的重复。然后,此功能将应用于我们的专栏plot_sub

# Function to create the df_sub
# Takes the plot_sub, original dataframe (df) and a list of columns, which should be compared
# Returns the desired df_sub with new interactions of species which are in plot_sub
# Only unique interactions are returned

create_df_sub <-  function(plot_sub, df, col_list){
  # Filter df such that it only contains species which are in plot_sub
  for (x in col_list) {
    df <- df[df[[x]] %in% plot_sub[[x]], ]
  }

  # Combine plot_sub and filtered df
  df_sub <- rbind(plot_sub[, col_list], df[, col_list]) 
  # Paste relevant colums together
  df_sub$paste_col <- do.call(paste, c(df_sub[, col_list], sep = '_'))
  # Exclude duplicated values
  df_sub <- df_sub[!duplicated(df_sub$paste_col), ]

  return(df_sub)
}

现在我定义要创建df_sub的列,然后将该函数应用于plot_sub - 列

col_list1 <- c('plantsp', 'lepsp')
col_list2 <- c('lepsp', 'psitsp')
col_list3 <- c('plantsp', 'lepsp', 'psitsp')

new_df <- new_df %>% 
  mutate(df_sub1 = map(plot_sub, create_df_sub, df = df, col_list = col_list1), 
         df_sub2 = map(plot_sub, create_df_sub, df = df, col_list = col_list2), 
         df_sub3 = map(plot_sub, create_df_sub, df = df, col_list = col_list3)) 

map将vector或list作为参数,并将指定的函数应用于每个元素(如lapply)。比较df_sub1plot_sub的第一个元素以查看差异。

new_df$plot_sub[[1]]
# A tibble: 5 x 3
#      plantsp    lepsp    psitsp
#        <chr>    <chr>     <chr>
# 1  plantsp_2 lepsp_19 psitsp_19
# 2 plantsp_21 lepsp_19  psitsp_4
# 3 plantsp_19  lepsp_2 psitsp_11
# 4  plantsp_9 lepsp_13 psitsp_24
# 5 plantsp_24  lepsp_9  psitsp_2

new_df$df_sub1[[1]]
# A tibble: 6 x 3
#      plantsp    lepsp           paste_col
#        <chr>    <chr>               <chr>
# 1  plantsp_2 lepsp_19  plantsp_2_lepsp_19
# 2 plantsp_21 lepsp_19 plantsp_21_lepsp_19
# 3 plantsp_19  lepsp_2  plantsp_19_lepsp_2
# 4  plantsp_9 lepsp_13  plantsp_9_lepsp_13
# 5 plantsp_24  lepsp_9  plantsp_24_lepsp_9
# 6  plantsp_9  lepsp_2   plantsp_9_lepsp_2

新的互动已添加到df_sub1

要提取匹配和唯一值,我在inner_join - 列和不同的anti_join&#39>上使用plot_subdf_sub

new_df <- new_df %>%
  mutate(match1 = map2(df_sub1, plot_sub, inner_join, by = col_list1), 
         match2 = map2(df_sub2, plot_sub, inner_join, by = col_list2), 
         match3 = map2(df_sub3, plot_sub, inner_join, by = col_list3), 
         unique1 = map2(df_sub1, plot_sub, anti_join, by = col_list1), 
         unique2 = map2(df_sub2, plot_sub, anti_join, by = col_list2), 
         unique3 = map2(df_sub3, plot_sub, anti_join, by = col_list3)) 

inner_join返回所有行,这些行在by - 参数中指定的列中具有匹配值,而anti_join返回df_sub的所有行,这些行是map2不匹配。 这里我使用new_df$match1[[1]] # A tibble: 5 x 4 # plantsp lepsp psitsp paste_col # <chr> <chr> <chr> <chr> # 1 plantsp_2 lepsp_19 psitsp_19 plantsp_2_lepsp_19 # 2 plantsp_21 lepsp_19 psitsp_4 plantsp_21_lepsp_19 # 3 plantsp_19 lepsp_2 psitsp_11 plantsp_19_lepsp_2 # 4 plantsp_9 lepsp_13 psitsp_24 plantsp_9_lepsp_13 # 5 plantsp_24 lepsp_9 psitsp_2 plantsp_24_lepsp_9 new_df$unique1[[1]] # A tibble: 1 x 3 # plantsp lepsp paste_col # <chr> <chr> <chr> # 1 plantsp_9 lepsp_2 plantsp_9_lepsp_2 - 函数,它接受两个向量/列表并应用指定的函数。

match

在最后一步中,我提取每个uniqueres_vec的行数并总结。我还计算new_df <- new_df %>% mutate(sum_match = map_int(match1, nrow) + map_int(match2, nrow) + map_int(match3, nrow), sum_unique = map_int(unique1, nrow) + map_int(unique2, nrow) + map_int(unique3, nrow), res_vec = ((sum_match + sum_unique)/((2*sum_match + sum_unique)/2)) - 1)

map_int

这里我使用map,因为我的返回值是一个整数,我想直接在总和中使用它。仅使用new_df %>% select(region, plot, sum_match, sum_unique, res_vec) # A tibble: 5 x 5 # region plot sum_match sum_unique res_vec # <dbl> <fctr> <int> <int> <dbl> # 1 1 A 15 1 0.03225806 # 2 1 B 27 3 0.05263158 # 3 1 C 6 2 0.14285714 # 4 2 D 18 1 0.02702703 # 5 2 E 6 0 0.00000000 将返回一个列表,我首先要将其转换为整数向量。

set.seed(540)
df <- data.frame(region = c(rep(1, 16), rep(2, 8)), 
                plot = c(rep('A', 5), rep('B', 9), rep('C', 2), rep('D', 6),rep('E', 2)), 
                plantsp = sample(1:24, 24, replace = TRUE), 
                lepsp = sample(1:24, 24, replace = TRUE), 
                psitsp = sample(1:24, 24, replace = TRUE))
df$plantsp <- paste('plantsp', df$plantsp, sep = '_')
df$lepsp <- paste('lepsp', df$lepsp, sep = '_')
df$psitsp <- paste('psitsp', df$psitsp, sep = '_')

数据

matrix = [[None]*length]*length