r - dplyr mutate - 如何使用 mutate 正确应用自定义函数?
问题描述
我正在尝试迁移数据库并希望使用 R 来协助该过程。作为迁移过程的一部分,我需要更新“项目 ID”,因为它们已更改。我创建了一个函数来将旧 ID 映射到新 ID:
old_to_new <- function(id, df) {
return (df[which(df$Old == id), ]$New)
}
但是,每当我尝试应用它来在我的数据框中添加一个新列(从数据库表加载)时:
library(tidyverse)
library(RODBC)
cn <- odbcDriverConnect(connection="Driver={SQL Server Native Client 11.0};server=xxx;database=xxx;uid=xxx;pwd=xxx;")
df <- sqlQuery(cn, "SELECT * FROM [MaintDB_New].[dbo].[Priority]")
ticket_df <- sqlQuery(cn, "SELECT * FROM [MaintDB_New].[dbo].[Tickets]")
ticket_details_df <- sqlQuery(cn, "SELECT * FROM [MaintDB_New].[dbo].[Ticket_Details]")
new_items <- read_csv("./ticket_itm_export_temp.csv", col_names = c("Old", "Name", "New"))
ticket_df_new <- ticket_df %>% mutate(item_id = old_to_new(itemID, new_items))
我收到以下错误:
Error in `[[<-.data.frame`(`*tmp*`, col, value = c(NA_integer_, NA_integer_, :
replacement has 280 rows, data has 69430
In addition: Warning message:
In df$Old == id :
longer object length is not a multiple of shorter object length
我做错了什么,正确的方法是什么。我在尝试使用ddplyr
.
我是 R 新手,所以如果这是一个明显的问题,我深表歉意。
编辑 - 添加数据结构:
head(ticket_df)
ticketID propertyID itemID roomNumber assignedToID isOpen openID latestID
1 11 10 1 <NA> NA 0 22 23
2 12 17 1 <NA> NA 0 24 289
3 13 17 1 <NA> NA 0 25 292
4 14 17 17 <NA> NA 0 26 4411
5 15 17 68 <NA> NA 0 27 296
6 16 17 74 <NA> NA 0 28 294
head(new_items)
Old Name New
<int> <chr> <int>
1 257 Register Cash Drawers 425
2 253 Alarm System 426
3 135 CREDENZA/ ARMOIRE 427
4 55 Back Office PC 428
5 183 Backup All Data 429
6 260 Base Boards 430
解决方案
我(真的!)认为 Gregor 对left_join
ing 的评论很有道理。我将通过更改您的一些值来强制进行一些匹配:
new_items$Old[1:2] <- c(17L,74L)
现在加入:
library(dplyr)
ticket_df %>%
left_join(select(new_items, Old, New), by=c("itemID" = "Old"))
# ticketID propertyID itemID roomNumber assignedToID isOpen openID latestID New
# 1 11 10 1 NA NA 0 22 23 NA
# 2 12 17 1 NA NA 0 24 289 NA
# 3 13 17 1 NA NA 0 25 292 NA
# 4 14 17 17 NA NA 0 26 4411 425
# 5 15 17 68 NA NA 0 27 296 NA
# 6 16 17 74 NA NA 0 28 294 426
如果您对此感到满意,只需重新分配:
ticket_df %>%
left_join(select(new_items, Old, New), by=c("itemID" = "Old")) %>%
mutate(itemID = if_else(is.na(New), itemID, New)) %>%
select(-New)
# ticketID propertyID itemID roomNumber assignedToID isOpen openID latestID
# 1 11 10 1 NA NA 0 22 23
# 2 12 17 1 NA NA 0 24 289
# 3 13 17 1 NA NA 0 25 292
# 4 14 17 425 NA NA 0 26 4411
# 5 15 17 68 NA NA 0 27 296
# 6 16 17 426 NA NA 0 28 294
或者,您可以使用mutate(itemID = coalesce(New, itemID))
,谢谢@Gregor。
但是,如果您需要使用一个函数(也许您的问题更复杂或者您需要更通用的东西),那么请注意:
- 通常,其中使用的函数
mutate
需要返回长度为 1 或与给定长度相同的向量;这意味着子集(就像您对 所做的那样df[which(df$Old == id), ]$New
)通常不起作用。(如果你能保证它总是返回长度 1 那么它就不会出错,但我猜这不安全。)。同样,summarize
需要(我相信)函数返回长度 1。
这是一个有点草率但得到相同结果的想法:
myfunc <- function(id, changes) {
ind <- match(id, changes[["Old"]])
indnonna <- !is.na(ind)
id[which(indnonna)] <- changes[["New"]][ind[indnonna]]
id
}
ticket_df %>%
mutate(newid = myfunc(itemID, new_items))
# ticketID propertyID itemID roomNumber assignedToID isOpen openID latestID newid
# 1 11 10 1 NA NA 0 22 23 1
# 2 12 17 1 NA NA 0 24 289 1
# 3 13 17 1 NA NA 0 25 292 1
# 4 14 17 17 NA NA 0 26 4411 425
# 5 15 17 68 NA NA 0 27 296 68
# 6 16 17 74 NA NA 0 28 294 426
您显然可以直接分配给itemID
而不是不同的列。我仍然不鼓励这样做,因为(1)连接效率更高;(2)我想更多地使用这个函数,也许可以找到一个更健壮的方法;(3) 它将结构(即特定列名)硬编码new_items
到函数中,而进行连接允许您在连接时指定发生的情况,使代码紧挨着使用结构的元素。
推荐阅读
- facebook - Facebook 消息 Webhook 事件:如何识别消息是页面故事的回复/评论?
- java - 如何在jsonobject中存储图像
- flutter - SharedPreferences.getInstance() 总是返回 null
- android - Android按钮文本对齐不起作用
- r - 如何从一组 N 个对象中选择 n 个对象,最大化它们之间的成对距离之和
- postgresql - 如何安装和启动 PostgreSQL 作为独立的基于 Python 的应用程序的一部分?
- python - Pandas:如何找到列的分箱均值
- bash - 从文件中获取特定字符串
- docker - 如何在 kubectl set 映像上指定注册表凭据?
- c# - 透明表单莫名其妙只通过了一些触摸输入