Skip to content

Commit 82fcc13

Browse files
felixcheungshivaram
authored andcommitted
[SPARK-19130][SPARKR] Support setting literal value as column implicitly
## What changes were proposed in this pull request? ``` df$foo <- 1 ``` instead of ``` df$foo <- lit(1) ``` ## How was this patch tested? unit tests Author: Felix Cheung <felixcheung_m@hotmail.com> Closes #16510 from felixcheung/rlitcol. (cherry picked from commit d749c06) Signed-off-by: Shivaram Venkataraman <shivaram@cs.berkeley.edu>
1 parent 1022049 commit 82fcc13

File tree

3 files changed

+39
-5
lines changed

3 files changed

+39
-5
lines changed

R/pkg/R/DataFrame.R

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1721,14 +1721,21 @@ setMethod("$", signature(x = "SparkDataFrame"),
17211721
getColumn(x, name)
17221722
})
17231723

1724-
#' @param value a Column or \code{NULL}. If \code{NULL}, the specified Column is dropped.
1724+
#' @param value a Column or an atomic vector in the length of 1 as literal value, or \code{NULL}.
1725+
#' If \code{NULL}, the specified Column is dropped.
17251726
#' @rdname select
17261727
#' @name $<-
17271728
#' @aliases $<-,SparkDataFrame-method
17281729
#' @note $<- since 1.4.0
17291730
setMethod("$<-", signature(x = "SparkDataFrame"),
17301731
function(x, name, value) {
1731-
stopifnot(class(value) == "Column" || is.null(value))
1732+
if (class(value) != "Column" && !is.null(value)) {
1733+
if (isAtomicLengthOne(value)) {
1734+
value <- lit(value)
1735+
} else {
1736+
stop("value must be a Column, literal value as atomic in length of 1, or NULL")
1737+
}
1738+
}
17321739

17331740
if (is.null(value)) {
17341741
nx <- drop(x, name)
@@ -1941,10 +1948,10 @@ setMethod("selectExpr",
19411948
#'
19421949
#' @param x a SparkDataFrame.
19431950
#' @param colName a column name.
1944-
#' @param col a Column expression.
1951+
#' @param col a Column expression, or an atomic vector in the length of 1 as literal value.
19451952
#' @return A SparkDataFrame with the new column added or the existing column replaced.
19461953
#' @family SparkDataFrame functions
1947-
#' @aliases withColumn,SparkDataFrame,character,Column-method
1954+
#' @aliases withColumn,SparkDataFrame,character-method
19481955
#' @rdname withColumn
19491956
#' @name withColumn
19501957
#' @seealso \link{rename} \link{mutate}
@@ -1957,11 +1964,16 @@ setMethod("selectExpr",
19571964
#' newDF <- withColumn(df, "newCol", df$col1 * 5)
19581965
#' # Replace an existing column
19591966
#' newDF2 <- withColumn(newDF, "newCol", newDF$col1)
1967+
#' newDF3 <- withColumn(newDF, "newCol", 42)
19601968
#' }
19611969
#' @note withColumn since 1.4.0
19621970
setMethod("withColumn",
1963-
signature(x = "SparkDataFrame", colName = "character", col = "Column"),
1971+
signature(x = "SparkDataFrame", colName = "character"),
19641972
function(x, colName, col) {
1973+
if (class(col) != "Column") {
1974+
if (!isAtomicLengthOne(col)) stop("Literal value must be atomic in length of 1")
1975+
col <- lit(col)
1976+
}
19651977
sdf <- callJMethod(x@sdf, "withColumn", colName, col@jc)
19661978
dataFrame(sdf)
19671979
})

R/pkg/R/utils.R

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -863,3 +863,7 @@ basenameSansExtFromUrl <- function(url) {
863863
# then, strip extension by the last '.'
864864
sub("([^.]+)\\.[[:alnum:]]+$", "\\1", filename)
865865
}
866+
867+
isAtomicLengthOne <- function(x) {
868+
is.atomic(x) && length(x) == 1
869+
}

R/pkg/inst/tests/testthat/test_sparkSQL.R

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,6 +1001,17 @@ test_that("select operators", {
10011001
expect_equal(columns(df), c("name", "age", "age2"))
10021002
expect_equal(count(where(df, df$age2 == df$age * 2)), 2)
10031003

1004+
df$age2 <- 21
1005+
expect_equal(columns(df), c("name", "age", "age2"))
1006+
expect_equal(count(where(df, df$age2 == 21)), 3)
1007+
1008+
df$age2 <- c(22)
1009+
expect_equal(columns(df), c("name", "age", "age2"))
1010+
expect_equal(count(where(df, df$age2 == 22)), 3)
1011+
1012+
expect_error(df$age3 <- c(22, NA),
1013+
"value must be a Column, literal value as atomic in length of 1, or NULL")
1014+
10041015
# Test parameter drop
10051016
expect_equal(class(df[, 1]) == "SparkDataFrame", T)
10061017
expect_equal(class(df[, 1, drop = T]) == "Column", T)
@@ -1777,6 +1788,13 @@ test_that("withColumn() and withColumnRenamed()", {
17771788
expect_equal(length(columns(newDF)), 2)
17781789
expect_equal(first(filter(newDF, df$name != "Michael"))$age, 32)
17791790

1791+
newDF <- withColumn(df, "age", 18)
1792+
expect_equal(length(columns(newDF)), 2)
1793+
expect_equal(first(newDF)$age, 18)
1794+
1795+
expect_error(withColumn(df, "age", list("a")),
1796+
"Literal value must be atomic in length of 1")
1797+
17801798
newDF2 <- withColumnRenamed(df, "age", "newerAge")
17811799
expect_equal(length(columns(newDF2)), 2)
17821800
expect_equal(columns(newDF2)[1], "newerAge")

0 commit comments

Comments
 (0)