【问题标题】:Matching dataframes by id fails for some values [closed]对于某些值,按 id 匹配数据帧失败 [关闭]
【发布时间】:2017-04-28 11:09:14
【问题描述】:

我有一个包含几千个缺失值的非常大的数据集,如下所示:

df1:
                            doi journal year
1  10.1037/0002-9432.76.1.13    <NA>   NA
2  10.1037/0002-9432.76.1.13    <NA>   NA
3  10.1037/0002-9432.76.1.13    <NA>   NA
4 10.1037/0003-066X.60.8.750    <NA>   NA
5 10.1037/0003-066X.60.8.750    <NA>   NA
6 10.1037/0003-066X.60.8.750    <NA>   NA

我有另一个数据框,其中包含所有缺失的期刊名称和年份:

df2:
                          doi year                             journal
17  10.1037/0002-9432.76.1.13 2006 American Journal of Orthopsychiatry
18  10.1037/0002-9432.76.1.13 2006 American Journal of Orthopsychiatry
19  10.1037/0002-9432.76.1.13 2006 American Journal of Orthopsychiatry
31 10.1037/0003-066x.60.8.750 2005               American Psychologist
32 10.1037/0003-066x.60.8.750 2005               American Psychologist
33 10.1037/0003-066x.60.8.750 2005               American Psychologist

然而,当我尝试通过它们的 doi 值匹配两者时

df1$year[is.na(df1$year)] <- df2$year[match(df1$doi[is.na(df1$year)], df2$doi)]
df1$journal[is.na(df1$journal)] <- df2$journal[match(df1$doi[is.na(df1$journal)], df2$doi)]

这仅适用于某些人:

Result:
                             doi                             journal year
1  10.1037/0002-9432.76.1.13 American Journal of Orthopsychiatry 2006
2  10.1037/0002-9432.76.1.13 American Journal of Orthopsychiatry 2006
3  10.1037/0002-9432.76.1.13 American Journal of Orthopsychiatry 2006
4 10.1037/0003-066X.60.8.750                                <NA>   NA
5 10.1037/0003-066X.60.8.750                                <NA>   NA
6 10.1037/0003-066X.60.8.750                                <NA>   NA

我尝试了不同的方法来匹配数据帧(如thisthis),以及在加载数据帧时修剪空白,但没有成功。 “doi”和“journal”是字符向量,“year”是一个整数。如果有人有一些见解,非常感谢。

【问题讨论】:

  • R 区分大小写,因此您的问题在于 doi 在值中显示的方式产生 NA,您在一个 df 中的 doi 中有大写 X,但在另一个中没有
  • 引用@Sarina 的评论:使用tolower 像这样match(df1$doi[is.na(df1$year)], tolower(df2$doi)) 可以解决问题。
  • 你可能还需要trimws

标签: r


【解决方案1】:

OP 提到他有一个非常大的数据集,其中有几千个缺失值。

这就是为什么我觉得建议data.table 解决方案使用在加入时更新 而不是基本R match(),尽管实际问题已经通过使用tolower() 解决了。 p>

library(data.table)

#prepare doi
setDT(df1)[, doi := tolower(doi)]
setDT(df2)[, doi := tolower(doi)]

#join
df1[unique(df2), on = "doi", `:=`(year = i.year, journal = i.journal)]

df1
#                          doi                             journal year
#1:  10.1037/0002-9432.76.1.13 American Journal of Orthopsychiatry 2006
#2:  10.1037/0002-9432.76.1.13 American Journal of Orthopsychiatry 2006
#3:  10.1037/0002-9432.76.1.13 American Journal of Orthopsychiatry 2006
#4: 10.1037/0003-066X.60.8.750               American Psychologist 2005
#5: 10.1037/0003-066X.60.8.750               American Psychologist 2005
#6: 10.1037/0003-066X.60.8.750               American Psychologist 2005

请注意,这会将df1df1yearjournal所有 值替换为df2 中给出的值,以匹配doi,无论它们是NA 还是不是。

有一个变体只替换 NA 的值:

df1[unique(df2), on = "doi", 
    `:=`(year = replace(year, is.na(year), i.year), 
         journal = replace(journal, is.na(journal), i.journal))]

基准测试

为了比较这三种方法的速度,df1 已附加到自身,因此它有大约 100'000 行。由于df1 本身已更新,因此每次运行基准测试都必须从一个新副本开始。复制操作也包含在基准测试中。

microbenchmark::microbenchmark(
  copy = df1 <- copy(df1_orig),
  OP_match = {
    df1 <- copy(df1_orig)
    df1$year[is.na(df1$year)] <- df2$year[match(df1$doi[is.na(df1$year)], df2$doi)]
    df1$journal[is.na(df1$journal)] <- df2$journal[match(df1$doi[is.na(df1$journal)], df2$doi)]
  },
  update_on_join = {
    df1 <- copy(df1_orig)
    df1[unique(df2), on = "doi", `:=`(year = i.year, journal = i.journal)]
  },
  replace_on_join = {
    df1 <- copy(df1_orig)
    df1[unique(df2), on = "doi", 
        `:=`(year = replace(year, is.na(year), i.year), 
             journal = replace(journal, is.na(journal), i.journal))]
  },
  times = 100L
)

结果表明,在这种情况下,_update_on_join_ 比使用 match() 的基本 R 快近三倍:

Unit: microseconds
            expr       min        lq      mean    median        uq        max neval
            copy   760.449   978.691  1129.290  1071.388  1202.974   2085.383   100
        OP_match 12376.362 14532.352 16215.333 15295.821 17497.497  35352.941   100
  update_on_join  5101.879  5585.939  6136.479  5914.435  6416.240   9272.643   100
 replace_on_join  7998.306  8729.303 11822.586  9367.416  9802.767 227385.521   100

数据

library(data.table)
df1 <- fread(
  "rn                    doi journal year
1  10.1037/0002-9432.76.1.13    <NA>   NA
2  10.1037/0002-9432.76.1.13    <NA>   NA
3  10.1037/0002-9432.76.1.13    <NA>   NA
4  10.1037/0003-066X.60.8.750   <NA>   NA
5  10.1037/0003-066X.60.8.750   <NA>   NA
6  10.1037/0003-066X.60.8.750   <NA>   NA",
  drop = 1, na.strings = c("NA", "<NA>"))
df1[, journal := as.character(journal)]
df1[, year := as.integer(year)]

df2 <- fread(
  "rn,                       doi, year,                             journal
  17,  10.1037/0002-9432.76.1.13, 2006, American Journal of Orthopsychiatry
  18,  10.1037/0002-9432.76.1.13, 2006, American Journal of Orthopsychiatry
  19,  10.1037/0002-9432.76.1.13, 2006, American Journal of Orthopsychiatry
  31, 10.1037/0003-066x.60.8.750, 2005,               American Psychologist
  32, 10.1037/0003-066x.60.8.750, 2005,               American Psychologist
  33, 10.1037/0003-066x.60.8.750, 2005,               American Psychologist",
  drop = 1)

#prepare doi
df1[, doi := tolower(doi)]
df2[, doi := tolower(doi)]

#create benchmark data

df1_orig <- copy(df1)
df2_orig <- copy(df2)

for (i in seq_len(14L)) df1_orig <- rbind(df1_orig, df1_orig)
nrow(df1_orig)

【讨论】:

    【解决方案2】:

    非常感谢@Sarina 和@Jaap,你们的cmets 是正确的,tolower为我解决了这个问题。

    R 区分大小写,因此您的问题在于 doi 在产生 NA 的值中的显示方式,您在一个 df 中的 doi 中有一个大写 X,但在另一个中没有 - Sarina

    补充@Sarina 的评论:使用 tolower like this match(df1$doi[is.na(df1$year)], tolower(df2$doi)) 可以解决问题。 – 雅普

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-05-15
      • 1970-01-01
      • 2018-11-16
      • 1970-01-01
      • 2017-05-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多