下标越界 - 通用定义和解决方案?

81

在使用R编程时,我经常会遇到"subscript out of bounds"的错误信息。例如:

# Load necessary libraries and data
library(igraph)
library(NetData)
data(kracknets, package = "NetData")

# Reduce dataset to nonzero edges
krack_full_nonzero_edges <- subset(krack_full_data_frame, (advice_tie > 0 | friendship_tie > 0 | reports_to_tie > 0))

# convert to graph data farme 
krack_full <- graph.data.frame(krack_full_nonzero_edges) 

# Set vertex attributes
for (i in V(krack_full)) {
    for (j in names(attributes)) {
        krack_full <- set.vertex.attribute(krack_full, j, index=i, attributes[i+1,j])
    }
}

# Calculate reachability for each vertix
reachability <- function(g, m) {
    reach_mat = matrix(nrow = vcount(g), 
                       ncol = vcount(g))
    for (i in 1:vcount(g)) {
        reach_mat[i,] = 0
        this_node_reach <- subcomponent(g, (i - 1), mode = m)

        for (j in 1:(length(this_node_reach))) {
            alter = this_node_reach[j] + 1
            reach_mat[i, alter] = 1
        }
    }
    return(reach_mat)
}

reach_full_in <- reachability(krack_full, 'in')
reach_full_in

这会产生以下错误:Error in reach_mat[i, alter] = 1 : subscript out of bounds

然而,我的问题不是关于这段特定的代码(尽管解决它也会很有帮助),而是更为普遍的:

  • 下标越界错误的定义是什么?是什么导致了它?
  • 有没有一些通用的方法来处理这种类型的错误?

4
@January已经说得很清楚了。它的意思是你正在尝试获取一个不存在的东西,比如一列或一行。例如,假设你的表格有10行,但你的函数试图调用第15行。 - Ben
3
对于任何想了解这个特定代码片段(来自McFarland网络分析实验室)的人,这是因为igraph已经将其索引方案从基于0改为基于1,所以在'subcomponent'中的'(i-1)'应该改为'i'。 - one_observation
7个回答

114
这是因为您尝试访问数组的边界之外。我将向您展示如何调试此类错误。
  1. I set options(error=recover)
  2. I run reach_full_in <- reachability(krack_full, 'in') I get :

    reach_full_in <- reachability(krack_full, 'in')
    Error in reach_mat[i, alter] = 1 : subscript out of bounds
    Enter a frame number, or 0 to exit   
    1: reachability(krack_full, "in")
    
  3. I enter 1 and I get

     Called from: top level 
    
  4. I type ls() to see my current variables

      1] "*tmp*"           "alter"           "g"               
         "i"               "j"                     "m"              
        "reach_mat"       "this_node_reach"
    
现在,我将查看我的变量的维度:
Browse[1]> i
[1] 1
Browse[1]> j
[1] 21
Browse[1]> alter
[1] 22
Browse[1]> dim(reach_mat)
[1] 21 21

你会发现alter越界了。22 > 21。在这行代码中:
  reach_mat[i, alter] = 1

为了避免这种错误,我个人会这样做:
  • 尽量使用applyxx函数。它们比for更安全。
  • 我使用seq_along而不是1:n(1:0)
  • 如果可以的话,请尝试以向量化的方式思考解决方案,以避免使用mat[i,j]索引访问。

编辑向量化解决方案

例如,在这里,我看到你没有利用set.vertex.attribute是向量化的事实。

你可以替换为:

# Set vertex attributes
for (i in V(krack_full)) {
    for (j in names(attributes)) {
        krack_full <- set.vertex.attribute(krack_full, j, index=i, attributes[i+1,j])
    }
}

通过这个:
##  set.vertex.attribute is vectorized!
##  no need to loop over vertex!
for (attr in names(attributes))
      krack_full <<- set.vertex.attribute(krack_full, 
                                             attr, value = attributes[,attr])

我认为你需要首先使用 options(error=function() dump.frames(to.file=TRUE)) 将错误日志记录在某个地方。 - Léo Léopold Hertz 준영
8
这个调试工具非常有用。在我的情况下,它使我发现“下标越界”错误似乎是因为我使用“因子”而不是字符来按名称访问矩阵列。使用 as.character 解决了我的问题。奇怪的是,出错的代码是一个库的一部分,在类似的数据上我成功地使用了它。 - bli
1
@bli 也许你正在使用一些不再支持因子的新版本软件包。 - agstudy

4

这只是意味着alter>ncol(reach_mat)i>nrow(reach_mat),换句话说,您的索引超出了数组边界(i大于行数,或alter大于列数)。

运行以上测试以了解发生了什么以及何时发生。


4

以上回答仅是一种可能性:在这种情况下,你可能在调用一个对象,但由于某些原因该对象不可用于你的查询。例如,你可能会按行名或列名进行子集操作,当你请求的行或列不再是数据矩阵或数据框的一部分时,就会收到此错误消息。

解决方案:简而言之,你需要找到最后一个有效的行名或列名,并且下一个被调用的对象应该是无法找到的对象。

如果你运行类似于“foreach”的并行代码,则需要将代码转换为for循环以便进行故障排除。


4
如果有人需要帮助,我在使用purr::map()时遇到了一个问题。我写了一个函数,大概是这样的:
find_nearby_shops <- function(base_account) {
   states_table %>% 
        filter(state == base_account$state) %>% 
        left_join(target_locations, by = c('border_states' = 'state')) %>% 
        mutate(x_latitude = base_account$latitude,
               x_longitude = base_account$longitude) %>% 
        mutate(dist_miles = geosphere::distHaversine(p1 = cbind(longitude, latitude), 
                                                     p2 = cbind(x_longitude, x_latitude))/1609.344)
}

nearby_shop_numbers <- base_locations %>% 
    split(f = base_locations$id) %>% 
    purrr::map_df(find_nearby_shops) 

有时我会在示例中遇到这种错误,但大多数情况下不会出现。问题的根源在于base_locations表中的某些州(PR)在states_table中不存在,因此实际上我已经筛选掉了所有内容,并传递了一个空表给mutate方法。 故事的寓意是您可能有数据问题而不仅仅是代码问题(因此您可能需要清理数据)。感谢agstudy和zx8754在上面帮助调试的答案。

2

我有时会遇到相同的问题。由于我对 R 不像其他语言那样熟练,因此我只能回答你的第二个问题。我发现标准的for循环会产生一些意外的结果。例如:x = 0

for (i in 1:x) {
  print(i)
}

输出结果为:
[1] 1
[1] 0

例如,使用Python时,
for i in range(x):
  print i

什么也不做。循环不会被执行。

我原本以为如果x = 0,那么在R语言中,循环将不会被执行。然而,1:0是一个有效的数字范围。除了在for循环外加一个if语句之外,我还没有找到一个好的解决方法。


2
这就是为什么你不应该对可能为零的x使用for (i in 1:x)(虽然它按照定义执行了正确的操作,但并非你所期望的)。你应该使用for (seq_len(x))来获得你所需的行为。 - Glen_b

1
我觉得关键是回到代码中检查错误或不确定的更改,并专注于必需而非美好。

1
这是来自斯坦福SNA免费教程的内容,它指出... # 只能计算一个顶点的可达性。 要获取整个图的统计信息,请更改“vertex”的值手动或编写for循环。 (请记住,与R对象不同,igraph对象从0开始编号。) 好的,因此在使用igraph时,第一行/列为0而不是1,但矩阵从1开始,因此对于任何igraph下的计算,您需要x-1,如下所示 this_node_reach <- subcomponent(g,(i - 1),mode = m) 但对于alter计算,这里有一个拼写错误 alter = this_node_reach[j] + 1 删除+1,它就可以正常工作了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接