Skip to content

Conversation

@titicaca
Copy link
Contributor

What changes were proposed in this pull request?

Fix a bug in collect method for collecting timestamp column, the bug can be reproduced as shown in the following codes and outputs:

library(SparkR)
sparkR.session(master = "local")
df <- data.frame(col1 = c(0, 1, 2), 
                 col2 = c(as.POSIXct("2017-01-01 00:00:01"), NA, as.POSIXct("2017-01-01 12:00:01")))

sdf1 <- createDataFrame(df)
print(dtypes(sdf1))
df1 <- collect(sdf1)
print(lapply(df1, class))

sdf2 <- filter(sdf1, "col1 > 0")
print(dtypes(sdf2))
df2 <- collect(sdf2)
print(lapply(df2, class))

As we can see from the printed output, the column type of col2 in df2 is converted to numeric unexpectedly, when NA exists at the top of the column.

This is caused by method do.call(c, list), if we convert a list, i.e. do.call(c, list(NA, as.POSIXct("2017-01-01 12:00:01")), the class of the result is numeric instead of POSIXct.

Therefore, we need to cast the data type of the vector explicitly.

How was this patch tested?

The patch can be tested manually with the same code above.

@titicaca titicaca changed the title SPARK-19342 bug fixed in collect method for collecting timestamp column [SPARK-19342][SPARKR] bug fixed in collect method for collecting timestamp column Jan 24, 2017
@felixcheung
Copy link
Member

Thanks! I can verify this case and the fix.
Could you please add some tests for this?

@HyukjinKwon
Copy link
Member

HyukjinKwon commented Jan 24, 2017

(Oh. it was all written in the PR description... I removed my useless comments..)

vec <- do.call(c, colTail)
classVal <- class(vec)
vec <- c(rep(NA, valueIndex[1] - 1), vec)
class(vec) <- classVal
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, what happened here?
if you want to drop the NA and use the rest to infer the class you can do col[!is.na(col)]

@titicaca
Copy link
Contributor Author

Sure. Shall I add the tests in pkg/inst/tests/testthat/test_sparkSQL.R?

@felixcheung
Copy link
Member

yes. but please see my other comment

…mn, if column values are all NAs, the type is logical
@titicaca
Copy link
Contributor Author

Sorry for the late reply. I figured out that the tests failed because if a vector is with only NAs, the type is logical, therefore we cannot cast the type in that case. I have updated the codes and added some tests for that. Thank you for the advice.

@felixcheung
Copy link
Member

great! @shivaram could you get Jenkins to test this fix please? I don't seem to have the power to command it :)

@shivaram
Copy link
Contributor

Jenkins, retest this please

if (!is.null(PRIMITIVE_TYPES[[colType]]) && colType != "binary") {
vec <- do.call(c, col)
stopifnot(class(vec) != "list")
# If vec is an vector with only NAs, the type is logical
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the DataFrame column is of type string, shouldn't it converts to R as character (which can be all NA), even though the column only has NULL (which maps to NA in R)?

it seems with this change it would become logical in R instead of character.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. My first commit was trying to cast the column to its corresponding R data type explicitly, even if it is an vector with all NAs. However some existed tests were failed and expecting to get logical NA. For example

3. Failure: column functions (@test_sparkSQL.R#1280) ---------------------------
collect(select(df, first(df$age)))[[1]] not equal to NA.
Types not compatible: double vs logical
4. Failure: column functions (@test_sparkSQL.R#1282) ---------------------------
collect(select(df, first("age")))[[1]] not equal to NA.
Types not compatible: double vs logical

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In local R, if we try

df <- data.frame(x = c(0,1,2), y = c(NA, NA, 1))
class(head(df, 1)$y)

The output is still numeric instead of logical. But the existed test is expecting NA logical instead of NA numeric.

So is it necessary to correct the existed tests, for example @test_sparkSQL.R#1280
from expect_equal(collect(select(df, first(df$age)))[[1]], NA) to
expect_equal(collect(select(df, first(df$age)))[[1]], NA_real_)

@shivaram
Copy link
Contributor

Jenkins, ok to test

@SparkQA
Copy link

SparkQA commented Jan 25, 2017

Test build #71969 has finished for PR 16689 at commit 6a0eb3f.

  • This patch fails R style tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

…to PRIMITIVE_TYPE, in addition two existed tests (@test_sparkSQL.R#1280 and @test_sparkSQL.R#1282) are modfied
@SparkQA
Copy link

SparkQA commented Jan 25, 2017

Test build #71982 has finished for PR 16689 at commit 43e334a.

  • This patch fails R style tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

@titicaca
Copy link
Contributor Author

I have modified the codes and tests, including the existed tests @test_sparkSQL.R#1280 and @test_sparkSQL.R#1282.

Like in local R, now NA column of the SparkDataFrame will also be collected as its corresponding type instead of logical NA.

@felixcheung
Copy link
Member

Just to make sure you see this: #16689 (comment)

@SparkQA
Copy link

SparkQA commented Jan 31, 2017

Test build #72182 has finished for PR 16689 at commit 7903bb3.

  • This patch fails R style tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

@SparkQA
Copy link

SparkQA commented Jan 31, 2017

Test build #72186 has finished for PR 16689 at commit 8379c38.

  • This patch passes all tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

stopifnot(class(vec) != "list")
class(vec) <-
if (colType == "timestamp")
c("POSIXct", "POSIXt")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why should the class be c("POSIXct", "POSIXt") in this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because PRIMITIVE_TYPES[["timestamp"]] is POSIXct, it usually comes with POSIXt together. POSIXt is virtual class used to allow operations such as subtraction to mix the two classes POSIXct and POSIXlt.
The previous convertion will also convert timestamp to c("POSIXct", "POSIXt").

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks better if it won't affect other methods. I will try it. Thanks for the advice.

if (colType == "timestamp")
c("POSIXct", "POSIXt")
else
PRIMITIVE_TYPES[[colType]]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by setting these instead of having it inferred - does this break any existing behavior? does any type differ because of this line of change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently all tests are passed, except for the two modified tests with NA types as discussed before. The followings are the all type convertions from SparkDataframe to R data.frame, which have been tested in the existing tests in test_sparkSQL.R.

PRIMITIVE_TYPES <- as.environment(list(
  "tinyint" = "integer",
  "smallint" = "integer",
  "int" = "integer",
  "bigint" = "numeric",
  "float" = "numeric",
  "double" = "numeric",
  "decimal" = "numeric",
  "string" = "character",
  "binary" = "raw",
  "boolean" = "logical",
  "timestamp" = "POSIXct",
  "date" = "Date",
  # following types are not SQL types returned by dtypes(). They are listed here for usage
  # by checkType() in schema.R.
  # TODO: refactor checkType() in schema.R.
  "byte" = "integer",
  "integer" = "integer"
  ))

@SparkQA
Copy link

SparkQA commented Feb 1, 2017

Test build #72250 has finished for PR 16689 at commit 407c625.

  • This patch fails SparkR unit tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

@titicaca
Copy link
Contributor Author

titicaca commented Feb 2, 2017

I tried to modify the PRIMITIVE_TYPES for timestamp, but it had a side effect on coltypes method.

In test_sparkSQL.R#2262, expect_equal(coltypes(DF), c("integer", "logical", "POSIXct")), coltypes return a list instead of a vector because of the convertion from timestamp to c(POSIXct, POSIXt)

@felixcheung
Copy link
Member

felixcheung commented Feb 2, 2017

hmm, that's not a super big issue since vector and list is more or less the same in R.
I think it might be better if we are treating the type consistently, although it might be concerning if this is changing in a non-backward compatible manner.

let me try to find some time to test this out? thanks!

@SparkQA
Copy link

SparkQA commented Feb 4, 2017

Test build #72378 has finished for PR 16689 at commit d6d454e.

  • This patch passes all tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

@titicaca
Copy link
Contributor Author

titicaca commented Feb 4, 2017

Thanks. I tried to fix the method coltypes for the modification of the timestamp, and it can pass all the tests now.

@felixcheung
Copy link
Member

hmm, this seems like a reasonable approach. With these changes:

  • collect on timestamp would get c("POSIXct", "POSIXt")
  • coltypes output will not change

@shivaram what do you think?

@shivaram
Copy link
Contributor

shivaram commented Feb 8, 2017

@felixcheung @titicaca Just to make sure I understand, collect on timestamp was getting c("POSIXct", "POSIXt") even before this change ?

@titicaca
Copy link
Contributor Author

titicaca commented Feb 9, 2017

Yes, collect on timestamp was getting c("POSIXct", "POSIXt"). But when NA exists at the top of the timestamp column, it was getting numeric as I described in the PR description.

@shivaram
Copy link
Contributor

shivaram commented Feb 9, 2017

Ok - I think this sounds good then ! @felixcheung Let me know if you want me to take a look at the code as well or if not feel free to merge when you think its ready

@felixcheung
Copy link
Member

great, this is a good catch and thank you for fixing this @titicaca
merging to master, branch-2.1

asfgit pushed a commit that referenced this pull request Feb 12, 2017
…stamp column

## What changes were proposed in this pull request?

Fix a bug in collect method for collecting timestamp column, the bug can be reproduced as shown in the following codes and outputs:

```
library(SparkR)
sparkR.session(master = "local")
df <- data.frame(col1 = c(0, 1, 2),
                 col2 = c(as.POSIXct("2017-01-01 00:00:01"), NA, as.POSIXct("2017-01-01 12:00:01")))

sdf1 <- createDataFrame(df)
print(dtypes(sdf1))
df1 <- collect(sdf1)
print(lapply(df1, class))

sdf2 <- filter(sdf1, "col1 > 0")
print(dtypes(sdf2))
df2 <- collect(sdf2)
print(lapply(df2, class))
```

As we can see from the printed output, the column type of col2 in df2 is converted to numeric unexpectedly, when NA exists at the top of the column.

This is caused by method `do.call(c, list)`, if we convert a list, i.e. `do.call(c, list(NA, as.POSIXct("2017-01-01 12:00:01"))`, the class of the result is numeric instead of POSIXct.

Therefore, we need to cast the data type of the vector explicitly.

## How was this patch tested?

The patch can be tested manually with the same code above.

Author: titicaca <[email protected]>

Closes #16689 from titicaca/sparkr-dev.

(cherry picked from commit bc0a0e6)
Signed-off-by: Felix Cheung <[email protected]>
@asfgit asfgit closed this in bc0a0e6 Feb 12, 2017
@felixcheung
Copy link
Member

@titicaca do you have a JIRA id on https://issues.apache.org? We would resolve the bug to you.

@titicaca
Copy link
Contributor Author

titicaca commented Feb 13, 2017

Yes. The JIRA id is SPARK-19342. This is my first commit to SPARK project. Thank you for the help and advices :)

@srowen
Copy link
Member

srowen commented Feb 13, 2017

@titicaca he means, what is your user ID on JIRA? so we can credit you. It's clear what the JIRA is.

@titicaca
Copy link
Contributor Author

Thanks for the reminder. I may have forgotten to mention that I am the reporter of this JIRA bug. My JIRA ID is also titicaca. Thank you!

@felixcheung
Copy link
Member

Great, done!
Looking forward to more contributions from you :)

cmonkey pushed a commit to cmonkey/spark that referenced this pull request Feb 15, 2017
…stamp column

## What changes were proposed in this pull request?

Fix a bug in collect method for collecting timestamp column, the bug can be reproduced as shown in the following codes and outputs:

```
library(SparkR)
sparkR.session(master = "local")
df <- data.frame(col1 = c(0, 1, 2),
                 col2 = c(as.POSIXct("2017-01-01 00:00:01"), NA, as.POSIXct("2017-01-01 12:00:01")))

sdf1 <- createDataFrame(df)
print(dtypes(sdf1))
df1 <- collect(sdf1)
print(lapply(df1, class))

sdf2 <- filter(sdf1, "col1 > 0")
print(dtypes(sdf2))
df2 <- collect(sdf2)
print(lapply(df2, class))
```

As we can see from the printed output, the column type of col2 in df2 is converted to numeric unexpectedly, when NA exists at the top of the column.

This is caused by method `do.call(c, list)`, if we convert a list, i.e. `do.call(c, list(NA, as.POSIXct("2017-01-01 12:00:01"))`, the class of the result is numeric instead of POSIXct.

Therefore, we need to cast the data type of the vector explicitly.

## How was this patch tested?

The patch can be tested manually with the same code above.

Author: titicaca <[email protected]>

Closes apache#16689 from titicaca/sparkr-dev.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants