-
Notifications
You must be signed in to change notification settings - Fork 393
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improved datetime unit transformer shortcuts #316
Conversation
Codecov Report
@@ Coverage Diff @@
## master #316 +/- ##
=========================================
+ Coverage 80.89% 82.19% +1.3%
=========================================
Files 325 327 +2
Lines 10581 10593 +12
Branches 349 550 +201
=========================================
+ Hits 8559 8707 +148
+ Misses 2022 1886 -136
Continue to review full report at Codecov.
|
* @return Integer feature of time period value | ||
*/ | ||
def toTimePeriod(period: TimePeriod): FeatureLike[Integral] = { | ||
val periodFun: Long => Int = period match { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets add a concrete transformer class that extends UnaryTransformer
instead of using UnaryLambdaTransformer
) | ||
} | ||
|
||
def toDayOfMonth(): FeatureLike[Integral] = toTimePeriod(TimePeriod.DayOfMonth) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there much value of having these methods? let's just have def toTimePeriod(period: TimePeriod)
and that way you wont need this abstract class bu simply add toTimePeriod
to both RichDateTimeFeature
and RichDateFeature
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so. As a user, I would much prefer to use these shortcuts and likely not use the time period shortcuts at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once introduced, we would have to maintain them ;) and each time a new entry in enum is added we would need to add a new method. that's why I just prefer a single method with an enum argument.
import org.scalatest.junit.JUnitRunner | ||
|
||
@RunWith(classOf[JUnitRunner]) | ||
class RichDateFeatureTest extends FlatSpec with TestSparkContext { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
once you have a concrete transformer, please use OpTransformerSpec
to test it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some minor comments, otherwise lgtm
@@ -0,0 +1,41 @@ | |||
package com.salesforce.op.stages.impl.feature |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add license header
)( | ||
implicit override val tti: TypeTag[I] | ||
) extends UnaryTransformer[I, Integral](operationName = "dateToTimePeriod", uid = uid){ | ||
val periodFun: Long => Int = period match { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just make this a regular function def periodConversion(time: Long): Int = period match { ... }
@crupley fyi, we also have |
Thanks for pointing that out; yes, I can add that in a new PR |
case TimePeriod.HourOfDay => new JDateTime(t, DateTimeUtils.DefaultTimeZone).hourOfDay.get | ||
case TimePeriod.MonthOfYear => new JDateTime(t, DateTimeUtils.DefaultTimeZone).monthOfYear.get | ||
case TimePeriod.WeekOfMonth => | ||
val dt = new JDateTime(t, DateTimeUtils.DefaultTimeZone) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JDateTime(t, DateTimeUtils.DefaultTimeZone)
is repeated on every case. Let us pull it before the pattern match.
)( | ||
implicit override val tti: TypeTag[I] | ||
) extends UnaryTransformer[I, Integral](operationName = "dateToTimePeriod", uid = uid){ | ||
def periodFun(t: Long): Int = period match { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would it make sense to use OOP and put
sealed abstract class TimePeriod extends EnumEntry with Serializable {
def periodFun(t: Long): Int
}
case object Blah extends TimePeriod {
override def periodFun(t: Long): Int = ???
}
and call period.periodFun
here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
put logic into enum? I am not a big fan of this pattern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea; let me try it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eliminates the case matching or having to pass a trait around with the conversion function in it. What do you think of the implementation, @tovbinm?
case object MonthOfYear extends TimePeriod | ||
case object WeekOfMonth extends TimePeriod | ||
case object WeekOfYear extends TimePeriod | ||
case object DayOfMonth extends TimePeriod { def extractFromTime(t: Long): Int = longToDateTime(t).dayOfMonth.get } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's easier to read if the function definition is after a line break.
case TimePeriod.MonthOfYear => Array(Integral(3), Integral(11), Integral(3), Integral.empty, Integral(4)) | ||
case TimePeriod.WeekOfMonth => Array(Integral(2), Integral(1), Integral(1), Integral.empty, Integral(4)) | ||
case TimePeriod.WeekOfYear => Array(Integral(11), Integral(45), Integral(10), Integral.empty, Integral(18)) | ||
case _ => throw new Exception(s"Unexpected TimePeriod encountered, $tp") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in tests instead of throw new Exception(s"Unexpected TimePeriod encountered, $tp")
do fail(s"Unexpected TimePeriod encountered, $tp")
Related issues
Improved datetime unit transformer shortcuts #315
Describe the proposed solution
Enhanced
RichDate
andRichDateTime
to allow conversion to various time periods using a common class. Each now supports atoTimePeriod
method which can convert to any of the current TimePeriods:TimePeriod.DayOfMonth
TimePeriod.DayOfWeek
TimePeriod.DayOfYear
TimePeriod.HourOfDay
TimePeriod.MonthOfYear
TimePeriod.WeekOfMonth
TimePeriod.WeekOfYear
Describe alternatives you've considered
There are many other potential values that could be extracted from the datetime, but I stuck to only those defined in the
TimePeriod
enum.Additional context