【问题标题】:handling enormous nested datasets in R / tidyr在 R / tidyr 中处理巨大的嵌套数据集
【发布时间】:2021-07-20 04:30:02
【问题描述】:
  • 我有使用神经网络进行单词识别模拟的数据文件。
  • 网络有 1000 个单词的词典。
  • 输出有 30,000 个节点 -- 每个单词以不同的对齐方式有 30 个副本。
  • 我以每个单词作为输入运行单独的模拟(1000 次模拟)。输出是这样的 100 步时间序列(此处显示 2 个输入示例(ark、bark),每个单词有 4 个副本,仅跟踪 4 个单词,并且仅显示 5 个时间步长)

[编辑:2021 年 5 月 3 日,数据集现在包含以前的解决方案无法处理的现实条件。对于更改数据,我深表歉意,但我没有找到更好的方法来澄清先前建议的解决方案中的差距。]

xf = read.table(header=T, sep=",", text="
Input,Time,Word,Copy1,Copy2,Copy3,Copy30
ark,10,ark,-0.1,-0.1,-0.1,-0.1
ark,20,ark,0.0,0.5,0.55,0.01
ark,30,ark,0.01,0.1,0.2,0.05
ark,40,ark,0.02,0.3,0.5,0.1
ark,50,ark,0.01,0.2,0.4,-0.1
ark,10,ad,-0.1,-0.1,-0.1,-0.1
ark,20,ad,0.0,0.01,0.02,0.01
ark,30,ad,0.01,0.03,0.1,0.04
ark,40,ad,0.02,0.12,0.15,0.04
ark,50,ad,0.01,0.01,0.05,0.02
ark,10,bark,-0.1,-0.1,-0.1,-0.1
ark,20,bark,0.02,0.12,0.1,0.01
ark,30,bark,0.03,0.15,0.12,0.02
ark,40,bark,0.02,0.22,0.1,0.03
ark,50,bark,0.01,0.1,0.05,0.02
ark,10,bar,-0.1,-0.1,-0.1,-0.1
ark,20,bar,0.01,0.1,0.02,-0.05
ark,30,bar,0.01,0.12,0.03,0
ark,40,bar,0.02,0.15,0.03,0.01
ark,50,bar,0.01,0.05,0.02,0.01
bark,10,ark,-0.1,-0.1,-0.1,-0.1
bark,20,ark,0.0,0.04,0.05,0.01
bark,30,ark,0.01,0.08,0.1,0.05
bark,40,ark,0.02,0.05,0.2,0.1
bark,50,ark,0.01,0.01,0.3,-0.1
bark,10,ad,-0.1,-0.1,-0.1,-0.1
bark,20,ad,0.0,0.01,0.01,0.01
bark,30,ad,0.01,0.02,0.05,0.04
bark,40,ad,0.02,0.03,0.06,0.04
bark,50,ad,0.01,0.02,0.01,0.02
bark,10,bark,-0.1,-0.1,-0.1,-0.1
bark,20,bark,0.02,0.15,0.1,0.01
bark,30,bark,0.03,0.3,0.12,0.02
bark,40,bark,0.02,0.7,0.1,0.03
bark,50,bark,0.01,0.7,0.05,0.02
bark,10,bar,-0.1,-0.1,-0.1,-0.1
bark,20,bar,0.01,0.13,0.04,-0.05
bark,30,bar,0.01,0.25,0.06,0
bark,40,bar,0.02,0.4,0.08,0.01
bark,50,bar,0.01,0.35,0.01,0.01
") %>% arrange(Input,Word,Time)

我想通过 2 种方式减少这些数据。

(1) 对于每个 Input x Word 组合,选择一个根据整个时间序列的最大值复制一个单词,然后

(2) 基于保留副本的最大值(每个输入 x 字 1 个),减少到“topX”字。


我最初的问题不清楚并且变得非常笨拙。 @DanChaltiel 使用非常接近完整解决方案的 pivot_longer 提供了部分答案,但我无法清楚地解释第一次减少。因此,我将其分解为separate question,@akrun 像这样扩展了@DanChaltiel 的解决方案,解决了第一部分(2021 年 5 月 3 日更新以反映对解决方案的修复):

library(tidyverse)
# Reduce data to one Copy of each Input x Word combination
# based on maxima for entire time series, no matter what
# Time those maxima occur. Using pivot_longer was due to 
# answer from @DanChaltiel, but getting it to work on 
# Input x Word maxima over the whole time series (rather 
# than maxima of Input x Word x Time) was due to @akrun 
# for https://stackoverflow.com/questions/67351185/
xf2 <- xf %>% 
  pivot_longer(cols = starts_with('Copy'), names_to = 'copy_name', 
               values_to = 'Value') %>% 
  group_by(Input, Time, Word) %>% 
  arrange(Value) %>%
  slice(if(all(Value <= 0)) n() 
        else tail(which(Value > 0), 1))%>% 
  group_by(Input, Word) %>% 
  mutate(copy_name = copy_name[which.max(Value)]) %>%
  ungroup

print((xf2 %>% arrange(Input, Word)), n = nrow(xf2)) # print all rows

# A tibble: 40 x 5
# Input  Time Word  copy_name Value
# <fct> <int> <fct> <chr>     <dbl>
# 1 ark       10 ad    Copy3     -0.1 
# 2 ark       20 ad    Copy3      0.02
# 3 ark       30 ad    Copy3      0.1 
# 4 ark       40 ad    Copy3      0.15
# 5 ark       50 ad    Copy3      0.05
# 6 ark       10 ark   Copy3     -0.1 
# 7 ark       20 ark   Copy3      0.55
# 8 ark       30 ark   Copy3      0.2 
# 9 ark       40 ark   Copy3      0.5 
# 10 ark      50 ark   Copy3      0.4 
# 11 ark      10 bar   Copy2     -0.1 
# 12 ark      20 bar   Copy2      0.1 
# 13 ark      30 bar   Copy2      0.12
# 14 ark      40 bar   Copy2      0.15
# 15 ark      50 bar   Copy2      0.05
# 16 ark      10 bark  Copy2     -0.1 
# 17 ark      20 bark  Copy2      0.12
# 18 ark      30 bark  Copy2      0.15
# 19 ark      40 bark  Copy2      0.22
# 20 ark      50 bark  Copy2      0.1 
# 21 bark     10 ad    Copy3     -0.1 
# 22 bark     20 ad    Copy3      0.01
# 23 bark     30 ad    Copy3      0.05
# 24 bark     40 ad    Copy3      0.06
# 25 bark     50 ad    Copy3      0.02
# 26 bark     10 ark   Copy3     -0.1 
# 27 bark     20 ark   Copy3      0.05
# 28 bark     30 ark   Copy3      0.1 
# 29 bark     40 ark   Copy3      0.2 
# 30 bark     50 ark   Copy3      0.3 
# 31 bark     10 bar   Copy2     -0.1 
# 32 bark     20 bar   Copy2      0.13
# 33 bark     30 bar   Copy2      0.25
# 34 bark     40 bar   Copy2      0.4 
# 35 bark     50 bar   Copy2      0.35
# 36 bark     10 bark  Copy2     -0.1 
# 37 bark     20 bark  Copy2      0.15
# 38 bark     30 bark  Copy2      0.3 
# 39 bark     40 bark  Copy2      0.7 
# 40 bark     50 bark  Copy2      0.7 

因此,这成功地将基于时间 1..100 系列中的最大值的每个 Input x Word 组合的数据减少到一个副本。

第二个挑战是将数据减少到每个输入的前 X 个单词。

@AnilGoyal 建议的方法适用于更简单的样本数据,但由于包含的时间步数与 topX 的值之间存在偶然的偶然性。

到目前为止,基于@AnilGoyal 的示例,我能够做的是根据每个输入的最大值识别topX 个单词。以下是查找前 3 名和前 2 名的 2 个示例:

topX = 3
xftop3 <- xf2 %>% group_by(Input, Word) %>%
  slice_max(Value, with_ties=FALSE) %>%
  arrange(desc(Value)) %>%
  group_by(Input) %>%
  filter(1:n() <= topX) %>%
  arrange(Input, Value)

xftop3

# A tibble: 6 x 5
# Groups:   Input [2]
# Input  Time Word  copy_name Value
# <fct> <int> <fct> <chr>     <dbl>
# 1 ark      40 ad    Copy3      0.15
# 2 ark      40 bark  Copy2      0.22
# 3 ark      20 ark   Copy3      0.55
# 4 bark     50 ark   Copy3      0.3 
# 5 bark     40 bar   Copy2      0.4 
# 6 bark     40 bark  Copy2      0.7 

topX = 2
xftop2 <- xf2 %>% group_by(Input, Word) %>%
  slice_max(Value, with_ties=FALSE) %>%
  arrange(desc(Value)) %>%
  group_by(Input) %>%
  filter(1:n() <= topX) %>%
  arrange(Input, Value)

xftop2

# A tibble: 4 x 5
# Groups:   Input [2]
# Input  Time Word  copy_name Value
# <fct> <int> <fct> <chr>     <dbl>
# 1 ark      40 bark  Copy2      0.22
# 2 ark      20 ark   Copy3      0.55
# 3 bark     40 bar   Copy2      0.4 
# 4 bark     40 bark  Copy2      0.7 

然后我不知道该怎么做是使用该 tibble 将数据集减少为始终只有那些 Input x Word 组合。样本数据和 topX = 2 的期望输出为:

# A tibble: 20 x 5
   Input  Time Word  copy_name Value
   <fct> <int> <fct> <chr>     <dbl>
 1 ark      10 ark   Copy3     -0.1 
 2 ark      20 ark   Copy3      0.55
 3 ark      30 ark   Copy3      0.2 
 4 ark      40 ark   Copy3      0.5 
 5 ark      50 ark   Copy3      0.4 
 6 ark      10 bark  Copy2     -0.1 
 7 ark      20 bark  Copy2      0.12
 8 ark      30 bark  Copy2      0.15
 9 ark      40 bark  Copy2      0.22
10 ark      50 bark  Copy2      0.1 
11 bark     10 bar   Copy2     -0.1 
12 bark     20 bar   Copy2      0.13
13 bark     30 bar   Copy2      0.25
14 bark     40 bar   Copy2      0.4 
15 bark     50 bar   Copy2      0.35
16 bark     10 bark  Copy2     -0.1 
17 bark     20 bark  Copy2      0.15
18 bark     30 bark  Copy2      0.3 
19 bark     40 bark  Copy2      0.7 
20 bark     50 bark  Copy2      0.7 

如果有任何建议,我将不胜感激。

【问题讨论】:

  • 您的问题相当复杂,您使用的是特定术语,例如激活。您可能想要重写它,以便使用常用术语,例如“这些列的最大值”。作为一个不精通神经网络的人,回答你的问题会容易得多。另外,您能否举例说明您的预期输出?
  • @DanChaltiel,谢谢你的建议。我编辑以按照您建议的方式澄清示例。谢谢!
  • 我试图简化事情。读起来还是太复杂了。每个输入固定的两个词的标准是什么?

标签: r dplyr tidyr


【解决方案1】:

到目前为止,我的理解是,您需要 semi_join,它仅根据右侧数据参数中可用的 row_combinations 过滤数据(左侧)。

topX = 2
semi_join(xf2, xf2 %>% group_by(Input, Word) %>%
  slice_max(Value, with_ties=FALSE) %>%
  group_by(Input) %>%
  slice_max(Value, n= topX, with_ties = FALSE) %>%
  select(Input, Word), by = c('Input', 'Word'))

# A tibble: 20 x 5
   Input  Time Word  copy_name Value
   <chr> <int> <chr> <chr>     <dbl>
 1 ark      10 ark   Copy3     -0.1 
 2 ark      10 bark  Copy2     -0.1 
 3 ark      20 ark   Copy3      0.55
 4 ark      20 bark  Copy2      0.12
 5 ark      30 ark   Copy3      0.2 
 6 ark      30 bark  Copy2      0.15
 7 ark      40 ark   Copy3      0.5 
 8 ark      40 bark  Copy2      0.22
 9 ark      50 ark   Copy3      0.4 
10 ark      50 bark  Copy2      0.1 
11 bark     10 bar   Copy2     -0.1 
12 bark     10 bark  Copy2     -0.1 
13 bark     20 bar   Copy2      0.13
14 bark     20 bark  Copy2      0.15
15 bark     30 bar   Copy2      0.25
16 bark     30 bark  Copy2      0.3 
17 bark     40 bar   Copy2      0.4 
18 bark     40 bark  Copy2      0.7 
19 bark     50 bar   Copy2      0.35
20 bark     50 bark  Copy2      0.7 

您的第二部分将通过此代码解决

topX <- 2L
xf2 %>% semi_join(xf2 %>% group_by(Input) %>%
  slice_max(Value, n= topX) %>% select(Input, Word), by = c("Input", "Word"))

# A tibble: 12 x 5
   Input  Time Word  copy_name Value
   <chr> <int> <chr> <chr>     <dbl>
 1 ark       1 ark   Copy3      0   
 2 ark       1 bark  Copy2      0   
 3 ark      50 ark   Copy3      0.05
 4 ark      50 bark  Copy2      0.06
 5 ark     100 ark   Copy3      0.55
 6 ark     100 bark  Copy2      0.2 
 7 bark      1 bar   Copy2      0   
 8 bark      1 bark  Copy2      0   
 9 bark     50 bar   Copy2      0.7 
10 bark     50 bark  Copy2      0.75
11 bark    100 bar   Copy2      0.4 
12 bark    100 bark  Copy2      0.6

我认为作为第一部分你也可以使用这个semi_join 请检查

xf %>% 
  pivot_longer(cols = starts_with('Copy'), names_to = 'copy_name', 
               values_to = 'Value') %>% 
  semi_join(xf %>% pivot_longer(cols = starts_with('Copy'), names_to = 'copy_name', 
                                values_to = 'Value') %>% 
              group_by(Input, Word) %>%
              slice_max(Value) %>%
              select(Input, Word, copy_name), 
            by = c('Input', 'Word', 'copy_name')) -> xf2

【讨论】:

  • 很好,@AnilGoyal!第一部分完美运行;输出正是我列为“所需输出”的内容。您的第二部分不起作用,因为它在 Input x Time x Word 处选择顶部副本,因此它在不同时间有不同的单词副本。如果将其输出与所需输出进行比较,您会发现它在多个地方与所需输出不同。我不知道是否有办法将这些组合成一个步骤。也许您应该修改以删除第二部分,然后我可以接受这个作为答案?
  • 再次感谢您,@AnilGoyal。对于第一部分,所需的输出是问题中print(xf2, n = nrow(xf2)) 之后打印的小标题。一步完成就可以了。我会将各种计时方法与真实数据集进行比较。到目前为止,我学到的一件事是,与我迄今为止尝试过的 map 方法相比,pivot_longer 速度非常快(大约是我尝试过的 future_map 方法的 3 倍)。
  • 您的解决方案使用了原始示例数据,因为包含的时间步数和各行的值存在意外情况。但是随着更多时间步长和更多单词(允许使用 topX > 2 的值)的扩展数据(参见修订后的问题),您的解决方案并不完全有效。我使用您的示例开发了一个部分解决方案,但我仍然坚持尝试为每个输入选择 topX 单词(基于整个时间序列中的那些 Input x Word 最大值)。
  • 非常感谢。正如我在上面对@DanChaltiel 的回应中指出的那样,您的答案和他的答案都可以可靠地产生正确的输出。我读到我应该接受我将在工作中实际使用的解决方案。使用真实数据,我的每个模拟的大小为 1000x1000x30x100,DanChaltiel 的解决方案大约快 50%,所以我将使用那个。我希望我能接受这两个答案。我非常感谢您对此提出的建议,当然,我赞成您的回答。
  • 没有问题。接受答案纯属个人选择。
【解决方案2】:

此答案已重写(两次),记录见版本日志。

您的问题有两个步骤:

    1. 找到最大值
    1. 选择与这些最大值相关的行

找到最大值是一个简单的过滤问题。但是,您可能希望根据所有时间的均值/中值来选择 Input-Word 对,而不是单次出现最大值。这将是一个总结的问题 (dplyr::summarise().

获得配对后,您只需选择正确的行。可能有很多方法,但我选择使用right_join()

出于教学目的,我选择将这些步骤分开,但您显然可以将它们合并到一个管道中。

topX=2
xf2bis = xf2 %>% 
  group_by(Input, Word) %>%
  filter(rank(Value, ties.method="first") == n()) %>% 
  group_by(Input) %>%
  filter(rank(Value, ties.method="first") > n() - topX) %>% 
  select(Input, Word)
xf2bis
#> # A tibble: 4 x 2
#> # Groups:   Input [2]
#>   Input Word 
#>   <chr> <chr>
#> 1 ark   ark  
#> 2 ark   bark 
#> 3 bark  bar  
#> 4 bark  bark

xftop2 = xf2 %>% 
  right_join(xf2bis, by=c("Input", "Word"))
  
xftop2 
#> # A tibble: 20 x 5
#>    Input  Time Word  copy_name Value
#>    <chr> <int> <chr> <chr>     <dbl>
#>  1 ark      10 ark   Copy3     -0.1 
#>  2 ark      10 bark  Copy2     -0.1 
#>  3 ark      20 ark   Copy3      0.55
#>  4 ark      20 bark  Copy2      0.12
#>  5 ark      30 ark   Copy3      0.2 
#>  6 ark      30 bark  Copy2      0.15
#>  7 ark      40 ark   Copy3      0.5 
#>  8 ark      40 bark  Copy2      0.22
#>  9 ark      50 ark   Copy3      0.4 
#> 10 ark      50 bark  Copy2      0.1 
#> 11 bark     10 bar   Copy2     -0.1 
#> 12 bark     10 bark  Copy2     -0.1 
#> 13 bark     20 bar   Copy2      0.13
#> 14 bark     20 bark  Copy2      0.15
#> 15 bark     30 bar   Copy2      0.25
#> 16 bark     30 bark  Copy2      0.3 
#> 17 bark     40 bar   Copy2      0.4 
#> 18 bark     40 bark  Copy2      0.7 
#> 19 bark     50 bar   Copy2      0.35
#> 20 bark     50 bark  Copy2      0.7

reprex package (v2.0.0) 于 2021-05-04 创建

【讨论】:

  • 啊——我刚刚意识到这个解决方案有一个小问题。我们需要的是选择每个单词的一个副本并丢弃所有其他单词。例如,在此解决方案中,在时间 1 选择 Copy1 作为树皮,而在时间 50 和 100 选择 Copy2。我正在寻找一种方法,可以找到具有单词最大值的 CopyX,然后仅保留该列并丢弃其他列。有什么建议?如果我能弄清楚,我会发布一个解决方案,但到目前为止我已经碰壁了。
  • 我已经编辑了原始帖子以澄清。
  • @user20412 抱歉,这有点难以理解,您介意分享ddf 的确切预期输出吗?
  • @user20412 不过,这里还有另一种可能更适合您需求的方法。我将根据您的预期输出更改分组。
  • 好的,很抱歉犹豫不决,但我读到我应该接受我将在工作中实际使用的解决方案。 Dan Chaltiel 的方法比 @AnilGoyal 的方法快约 50%(当我扩展到数十个大小为 1000x1000x30x100 的完整模拟时,这一点很重要),所以我会接受这个答案。我希望我能接受两者。我非常感谢所有的建议。
猜你喜欢
  • 2020-07-21
  • 1970-01-01
  • 2017-09-24
  • 1970-01-01
  • 2021-01-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多