Skip to content

Conversation

@viirya
Copy link
Member

@viirya viirya commented Feb 1, 2018

What changes were proposed in this pull request?

Seems R's substr API treats Scala substr API as zero based and so subtracts the given starting position by 1.

Because Scala's substr API also accepts zero-based starting position (treated as the first element), so the current R's substr test results are correct as they all use 1 as starting positions.

How was this patch tested?

Modified tests.

@viirya
Copy link
Member Author

viirya commented Feb 1, 2018

One more thing to notice is that the two parameters (starting and ending positions) of R's substr API is also unaligned with Scala's substr which takes starting position and substring length.

@viirya
Copy link
Member Author

viirya commented Feb 1, 2018

cc @felixcheung @HyukjinKwon

@srowen
Copy link
Member

srowen commented Feb 1, 2018

Also @shivaram

@shivaram
Copy link
Contributor

shivaram commented Feb 1, 2018

One thing to keep in mind is what the user's perception of the API is. If R users are going to use 1-based indexing then this might not be the right fix ? http://stat.ethz.ch/R-manual/R-devel/library/base/html/substr.html is the base R function FWIW

@viirya
Copy link
Member Author

viirya commented Feb 1, 2018

@shivaram This fix is to make it correctly 1-based. Previously SparkR substr API substracts starting position by 1, so it becomes zero-based.

This fix matches R's substr in above link as I test:

> substr("Michael", 4, 6)                                                                                                                  
[1] "hae"

Before this fix, SparkR's substr returns "cha".

@shivaram
Copy link
Contributor

shivaram commented Feb 1, 2018

Thanks for clarifying @viirya. Is the PR description accurate ? I read it as ..SQL's substr also accepts zero-based starting position while R uses a 1-based starting position.

@SparkQA
Copy link

SparkQA commented Feb 1, 2018

Test build #86908 has finished for PR 20464 at commit a2ffdc1.

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

@viirya
Copy link
Member Author

viirya commented Feb 1, 2018

@shivaram Thanks for pointing out it. I made change to the description. Hopefully it is clearer now. Basically I just want to clarify why R's substr tests are correct previously.

@HyukjinKwon
Copy link
Member

I was just manually double checking both substr in R and this. It seems correct; however, I think we should add a note in the doc and release note ...

One followup question is though, would it be difficult to match the behaviour with substr in R when the index is 0 or minus? If i understood #20464 (comment) correctly, it sounds better to match it to substr's behaviour in R. Took a quick look/test and seems we can just set start to 1 for both cases.

If this followup question is something we are not sure yet, I think we might be okay as is.

@HyukjinKwon
Copy link
Member

Just in case, I am testing with:

df <- createDataFrame(list(list(a="abcdef")))
collect(select(df, substr(df$a, 4, 5)))
substr("abcdef", 4, 5)

just in case it helps to check and reproduce.

@viirya
Copy link
Member Author

viirya commented Feb 1, 2018

One followup question is though, would it be difficult to match the behaviour with substr in R when the index is 0 or minus? If i understood #20464 (comment) correctly, it sounds better to match it to substr's behaviour in R. Took a quick look/test and seems we can just set start to 1 for both cases.

If we both consider the indices at starting and ending, setting them to 1 seems not enough. E.g.,

> substr("abcdef", -2, -3)
[1] ""
> substr("abcdef", 1, 1)
[1] "a"

For the cases when only ending is zero/negative, no matter what starting is, the result is empty string.

For the cases when only starting is zero/negative, we can set it to 1.

For the cases they are both zero/negative, the result is empty string.

We can address this in another PR.

setMethod("substr", signature(x = "Column"),
function(x, start, stop) {
jc <- callJMethod(x@jc, "substr", as.integer(start - 1), as.integer(stop - start + 1))
jc <- callJMethod(x@jc, "substr", as.integer(start), as.integer(stop - start + 1))
Copy link
Member

Choose a reason for hiding this comment

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

I'm a bit concern with changing this. As you can see it's been like this from the very beginning...

Copy link
Member Author

Choose a reason for hiding this comment

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

This API behavior should be considered as wrong and performs inconsistently. Because for starting position 1, we get substring from 1st element, but for position 2, we still get the substring from 1. So we will get the following inconsistent results:

> collect(select(df, substr(df$a, 1, 5)))
  substring(a, 0, 5)
1              abcde
> collect(select(df, substr(df$a, 2, 5)))
  substring(a, 1, 4)
1               abcd

For such change, we might need to add a note in the doc as @HyukjinKwon suggested.

Copy link
Member

Choose a reason for hiding this comment

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

question:

Copy link
Member Author

Choose a reason for hiding this comment

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

Just because I think it is another issue regarding 0/negative indices. I can deal it here if you strongly feel it is better.

Copy link
Member Author

Choose a reason for hiding this comment

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

is there a way to make the behavior the same before this change for any caller calling substr with common index like 0

Should we keep the behavior when calling substr with 0 as start index?

> df <- createDataFrame(list(list(a="abcdef")))
> collect(select(df, substr(df$a, 0, 5)))
  substring(a, -1, 6)
1                   f
> substr("abcdef", 0, 5)
[1] "abcde"

I think the previous behavior is pretty unreasonable..

@SparkQA
Copy link

SparkQA commented Feb 1, 2018

Test build #86924 has finished for PR 20464 at commit 95c8a4e.

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

docs/sparkr.md Outdated

## Upgrading to Spark 2.4.0

- The first parameter of `substr` method was wrongly subtracted by one, previously. This can lead to inconsistent substring results and also does not match with the behaviour with `substr` in R. It has been corrected.
Copy link
Member

Choose a reason for hiding this comment

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

instead of The first parameter of -> The ``start`` parameter of...

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

setMethod("substr", signature(x = "Column"),
function(x, start, stop) {
jc <- callJMethod(x@jc, "substr", as.integer(start - 1), as.integer(stop - start + 1))
jc <- callJMethod(x@jc, "substr", as.integer(start), as.integer(stop - start + 1))
Copy link
Member

Choose a reason for hiding this comment

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

question:

@SparkQA
Copy link

SparkQA commented Feb 2, 2018

Test build #86985 has finished for PR 20464 at commit d994d76.

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

Copy link
Member

@HyukjinKwon HyukjinKwon left a comment

Choose a reason for hiding this comment

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

I admit it's correct .. so LGTM but let me leave it to @felixcheung and @shivaram ..

@shivaram
Copy link
Contributor

shivaram commented Feb 7, 2018

I think @felixcheung has the most context here, so I'd suggest we wait for his comments.

@viirya
Copy link
Member Author

viirya commented Feb 15, 2018

ping @felixcheung

@felixcheung
Copy link
Member

Sorry, I'm a bit occupied with testing 2.3 RC, will get back to this after.

@viirya
Copy link
Member Author

viirya commented Feb 16, 2018

@felixcheung Thanks!

@viirya
Copy link
Member Author

viirya commented Mar 2, 2018

Because 2.3 is released, ping @felixcheung again

setMethod("substr", signature(x = "Column"),
function(x, start, stop) {
jc <- callJMethod(x@jc, "substr", as.integer(start - 1), as.integer(stop - start + 1))
jc <- callJMethod(x@jc, "substr", as.integer(start), as.integer(stop - start + 1))
Copy link
Member

Choose a reason for hiding this comment

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

I think we should do two things:

  1. add to the func doc that the start param should be 0-base and to add to the example with the result
    collect(select(df, substr(df$a, 0, 5))) # this should give you...

Copy link
Member Author

@viirya viirya Mar 6, 2018

Choose a reason for hiding this comment

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

I think you mean 1-base.

Copy link
Member Author

Choose a reason for hiding this comment

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

Added to the func doc.

docs/sparkr.md Outdated

## Upgrading to Spark 2.4.0

- The `start` parameter of `substr` method was wrongly subtracted by one, previously. This can lead to inconsistent substring results and also does not match with the behaviour with `substr` in R. It has been corrected.
Copy link
Member

Choose a reason for hiding this comment

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

  1. in the migration guide we should give a concrete example with non-0 start index, eg.
    substr(df$a, 1, 6) should be changed to substr(df$a, 0, 5)

@SparkQA
Copy link

SparkQA commented Mar 6, 2018

Test build #87993 has finished for PR 20464 at commit 0ebdf74.

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

@felixcheung
Copy link
Member

appveyor tests failed, could you close and reopen this PR to trigger it.

strange, I haven't seen anything like this on appveyor a long time.

1. Error: create DataFrame with complex types (@test_sparkSQL.R#535) -----------
8712org.apache.spark.SparkException: Job aborted due to stage failure: Task 0 in stage 114.0 failed 1 times, most recent failure: Lost task 0.0 in stage 114.0 (TID 116, localhost, executor driver): java.net.SocketTimeoutException: Accept timed out

@viirya viirya closed this Mar 6, 2018
@viirya viirya reopened this Mar 6, 2018
Copy link
Member

@felixcheung felixcheung left a comment

Choose a reason for hiding this comment

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

LG, pending tests.
one small comment for clarity. thanks!

docs/sparkr.md Outdated

## Upgrading to Spark 2.4.0

- The `start` parameter of `substr` method was wrongly subtracted by one, previously. In other words, the index specified by `start` parameter was considered as 0-base. This can lead to inconsistent substring results and also does not match with the behaviour with `substr` in R. It has been fixed so the `start` parameter of `substr` method is now 1-base, e.g., `substr(df$a, 2, 5)` should be changed to `substr(df$a, 1, 4)`.
Copy link
Member

Choose a reason for hiding this comment

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

could you add
method is now 1-base, e.g., therefore to get the same result as substr(df$a, 2, 5), it should be changed to substr(df$a, 1, 4)

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes. Added.

@SparkQA
Copy link

SparkQA commented Mar 7, 2018

Test build #88039 has finished for PR 20464 at commit 8c1a8ec.

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

@felixcheung
Copy link
Member

merged to master, thanks!

@asfgit asfgit closed this in 53561d2 Mar 7, 2018
HyukjinKwon pushed a commit to HyukjinKwon/spark that referenced this pull request May 6, 2018
…by 1 when calling Scala API

## What changes were proposed in this pull request?

Seems R's substr API treats Scala substr API as zero based and so subtracts the given starting position by 1.

Because Scala's substr API also accepts zero-based starting position (treated as the first element), so the current R's substr test results are correct as they all use 1 as starting positions.

## How was this patch tested?

Modified tests.

Author: Liang-Chi Hsieh <[email protected]>

Closes apache#20464 from viirya/SPARK-23291.
@viirya viirya deleted the SPARK-23291 branch December 27, 2023 18:21
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