Skip to content

Commit 1760372

Browse files
authored
introduce series (chitralverma#89)
* introduce series * allow date series * allow datetime series * fix build * allow list series * refactor series * add example for series * type tag for nested series and doc strings * support for java list series * add java example for series
1 parent d2f2d12 commit 1760372

File tree

16 files changed

+808
-22
lines changed

16 files changed

+808
-22
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ The project is mainly divided into 2 submodules,
4040
## Compatibility
4141

4242
- JDK version `>=8`
43-
- Scala version `2.12.x`, `2.13.x` and `3.3.x`. Default is `3.3.3`
43+
- Scala version `2.12.x`, `2.13.x` and `3.3.x`. Default is `2.13.x`
4444
- Rust version `>=1.58`
4545

4646
## Building

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ lazy val core = project
1616
.settings(name := "scala-polars")
1717
.enablePlugins(GhpagesPlugin, SiteScaladocPlugin)
1818
.settings(
19-
unidocSourceFilePatterns := Nil,
19+
// unidocSourceFilePatterns := Nil,
2020
git.remoteRepo := "[email protected]:chitralverma/scala-polars.git",
2121
SiteScaladoc / siteSubdirName := "api/latest"
2222
)
Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
package org.polars.scala.polars.api
2+
3+
import scala.jdk.CollectionConverters._
4+
import scala.util.Try
5+
6+
import org.polars.scala.polars.internal.jni.series
7+
8+
import izumi.reflect._
9+
10+
class Series private (private[polars] val ptr: Long) {
11+
12+
def show(): Unit = series.show(ptr)
13+
}
14+
15+
object Series {
16+
private final val EmptyString = ""
17+
18+
/** Initialize new nested series by name and values of type [[Series]].
19+
*
20+
* @param name
21+
* Name of Series
22+
* @param values
23+
* Values of Series
24+
*
25+
* @return
26+
* Nested Series of type [[Series]]. If `values` is empty, empty series is returned retaining
27+
* type.
28+
*/
29+
def of(name: String, values: Array[Series]): Series =
30+
Series.withPtr(series.new_list_series(name, values.map(_.ptr)))
31+
32+
/** Initialize new series by name and values of type [[Int]].
33+
*
34+
* @param name
35+
* Name of Series
36+
* @param values
37+
* Values of Series
38+
*
39+
* @return
40+
* Series of type [[Int]]. If `values` is empty, empty series is returned retaining type.
41+
*/
42+
def of(name: String, values: Array[Int]): Series =
43+
Series.withPtr(series.new_int_series(name, values))
44+
45+
/** Initialize new series by name and values of type [[java.lang.Integer]].
46+
*
47+
* @param name
48+
* Name of Series
49+
* @param values
50+
* Values of Series
51+
*
52+
* @return
53+
* Series of type [[java.lang.Integer]]. If `values` is empty, empty series is returned
54+
* retaining type.
55+
*/
56+
def of(name: String, values: Array[java.lang.Integer]): Series =
57+
Series.withPtr(series.new_int_series(name, values.map(_.intValue())))
58+
59+
/** Initialize new series by name and values of type [[Long]].
60+
*
61+
* @param name
62+
* Name of Series
63+
* @param values
64+
* Values of Series
65+
*
66+
* @return
67+
* Series of type [[Long]]. If `values` is empty, empty series is returned retaining type.
68+
*/
69+
def of(name: String, values: Array[Long]): Series =
70+
Series.withPtr(series.new_long_series(name, values))
71+
72+
/** Initialize new series by name and values of type [[java.lang.Long]].
73+
*
74+
* @param name
75+
* Name of Series
76+
* @param values
77+
* Values of Series
78+
*
79+
* @return
80+
* Series of type [[java.lang.Long]]. If `values` is empty, empty series is returned
81+
* retaining type.
82+
*/
83+
def of(name: String, values: Array[java.lang.Long]): Series =
84+
Series.withPtr(series.new_long_series(name, values.map(_.longValue())))
85+
86+
/** Initialize new series by name and values of type [[Float]].
87+
*
88+
* @param name
89+
* Name of Series
90+
* @param values
91+
* Values of Series
92+
*
93+
* @return
94+
* Series of type [[Float]]. If `values` is empty, empty series is returned retaining type.
95+
*/
96+
def of(name: String, values: Array[Float]): Series =
97+
Series.withPtr(series.new_float_series(name, values))
98+
99+
/** Initialize new series by name and values of type [[java.lang.Float]].
100+
*
101+
* @param name
102+
* Name of Series
103+
* @param values
104+
* Values of Series
105+
*
106+
* @return
107+
* Series of type [[java.lang.Float]]. If `values` is empty, empty series is returned
108+
* retaining type.
109+
*/
110+
def of(name: String, values: Array[java.lang.Float]): Series =
111+
Series.withPtr(series.new_float_series(name, values.map(_.floatValue())))
112+
113+
/** Initialize new series by name and values of type [[Double]].
114+
*
115+
* @param name
116+
* Name of Series
117+
* @param values
118+
* Values of Series
119+
*
120+
* @return
121+
* Series of type [[Double]]. If `values` is empty, empty series is returned retaining type.
122+
*/
123+
def of(name: String, values: Array[Double]): Series =
124+
Series.withPtr(series.new_double_series(name, values))
125+
126+
/** Initialize new series by name and values of type [[java.lang.Double]].
127+
*
128+
* @param name
129+
* Name of Series
130+
* @param values
131+
* Values of Series
132+
*
133+
* @return
134+
* Series of type [[java.lang.Double]]. If `values` is empty, empty series is returned
135+
* retaining type.
136+
*/
137+
def of(name: String, values: Array[java.lang.Double]): Series =
138+
Series.withPtr(series.new_double_series(name, values.map(_.doubleValue())))
139+
140+
/** Initialize new series by name and values of type [[Boolean]].
141+
*
142+
* @param name
143+
* Name of Series
144+
* @param values
145+
* Values of Series
146+
*
147+
* @return
148+
* Series of type [[Boolean]]. If `values` is empty, empty series is returned retaining type.
149+
*/
150+
def of(name: String, values: Array[Boolean]): Series =
151+
Series.withPtr(series.new_boolean_series(name, values))
152+
153+
/** Initialize new series by name and values of type [[java.lang.Boolean]].
154+
*
155+
* @param name
156+
* Name of Series
157+
* @param values
158+
* Values of Series
159+
*
160+
* @return
161+
* Series of type [[java.lang.Boolean]]. If `values` is empty, empty series is returned
162+
* retaining type.
163+
*/
164+
def of(name: String, values: Array[java.lang.Boolean]): Series =
165+
Series.withPtr(series.new_boolean_series(name, values.map(_.booleanValue())))
166+
167+
/** Initialize new series by name and values of type [[String]].
168+
*
169+
* @param name
170+
* Name of Series
171+
* @param values
172+
* Values of Series
173+
*
174+
* @return
175+
* Series of type [[String]]. If `values` is empty, empty series is returned retaining type.
176+
*/
177+
def of(name: String, values: Array[String]): Series =
178+
Series.withPtr(series.new_str_series(name, values))
179+
180+
/** Initialize new series by name and values of type [[java.time.LocalDate]].
181+
*
182+
* @param name
183+
* Name of Series
184+
* @param values
185+
* Values of Series
186+
*
187+
* @return
188+
* Series of type [[java.time.LocalDate]]. If `values` is empty, empty series is returned
189+
* retaining type.
190+
*/
191+
def of(name: String, values: Array[java.time.LocalDate]): Series =
192+
Series.withPtr(
193+
series.new_date_series(
194+
name,
195+
values.map(v => java.time.format.DateTimeFormatter.ISO_LOCAL_DATE.format(v))
196+
)
197+
)
198+
199+
/** Initialize new series by name and values of type [[java.time.LocalDateTime]].
200+
*
201+
* @param name
202+
* Name of Series
203+
* @param values
204+
* Values of Series
205+
*
206+
* @return
207+
* Series of type [[java.time.LocalDateTime]]. If `values` is empty, empty series is returned
208+
* retaining type.
209+
*/
210+
def of(name: String, values: Array[java.time.LocalDateTime]): Series =
211+
Series.withPtr(
212+
series.new_datetime_series(
213+
name,
214+
values.map(v => java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(v))
215+
)
216+
)
217+
218+
/** Initialize new nested series by name and values of provided type.
219+
*
220+
* @param name
221+
* Name of Series
222+
* @param values
223+
* Values of Series
224+
*
225+
* @return
226+
* Nested Series of provided type. If `values` is empty, empty series is returned retaining
227+
* type.
228+
*/
229+
def of[T: Tag](name: String, values: Array[Array[T]]): Series =
230+
Series.of(
231+
name,
232+
Try(values.map {
233+
case e: Array[Int] => Series.of(EmptyString, e)
234+
case e: Array[java.lang.Integer] => Series.of(EmptyString, e)
235+
case e: Array[Long] => Series.of(EmptyString, e)
236+
case e: Array[java.lang.Long] => Series.of(EmptyString, e)
237+
case e: Array[Float] => Series.of(EmptyString, e)
238+
case e: Array[java.lang.Float] => Series.of(EmptyString, e)
239+
case e: Array[Double] => Series.of(EmptyString, e)
240+
case e: Array[java.lang.Double] => Series.of(EmptyString, e)
241+
case e: Array[Boolean] => Series.of(EmptyString, e)
242+
case e: Array[java.lang.Boolean] => Series.of(EmptyString, e)
243+
case e: Array[java.time.LocalDate] => Series.of(EmptyString, e)
244+
case e: Array[java.time.LocalDateTime] => Series.of(EmptyString, e)
245+
case e: Array[String] => Series.of(EmptyString, e)
246+
case e: Array[Array[_]] => Series.of(EmptyString, e)
247+
}).getOrElse(
248+
throw new IllegalArgumentException(
249+
s"Nested series of provided internal type `${Tag[T].closestClass.getSimpleName}` is currently not supported."
250+
)
251+
)
252+
)
253+
254+
/** Initialize new nested series by name and values of provided type.
255+
*
256+
* @param name
257+
* Name of Series
258+
* @param values
259+
* Values of Series as a [[java.util.List]]
260+
*
261+
* @return
262+
* Nested Series of provided type. If `values` is empty, empty series is returned retaining
263+
* type.
264+
*/
265+
def of[T](name: String, values: java.util.List[java.util.List[T]]): Series = {
266+
267+
val data = values.asScala.toArray.map(_.toArray)
268+
269+
Series.of(
270+
name,
271+
Try(
272+
data.map(_.asInstanceOf[Array[_]]).map {
273+
case e: Array[Int] => Series.of(EmptyString, e)
274+
case e: Array[java.lang.Integer] => Series.of(EmptyString, e)
275+
case e: Array[Long] => Series.of(EmptyString, e)
276+
case e: Array[java.lang.Long] => Series.of(EmptyString, e)
277+
case e: Array[Float] => Series.of(EmptyString, e)
278+
case e: Array[java.lang.Float] => Series.of(EmptyString, e)
279+
case e: Array[Double] => Series.of(EmptyString, e)
280+
case e: Array[java.lang.Double] => Series.of(EmptyString, e)
281+
case e: Array[Boolean] => Series.of(EmptyString, e)
282+
case e: Array[java.lang.Boolean] => Series.of(EmptyString, e)
283+
case e: Array[java.time.LocalDate] => Series.of(EmptyString, e)
284+
case e: Array[java.time.LocalDateTime] => Series.of(EmptyString, e)
285+
case e: Array[String] => Series.of(EmptyString, e)
286+
case e: Array[Array[_]] => Series.of(EmptyString, e)
287+
}
288+
).getOrElse(
289+
throw new IllegalArgumentException(
290+
s"Nested series of provided internal type `${data.getClass.getSimpleName}` is currently not supported."
291+
)
292+
)
293+
)
294+
}
295+
296+
def withPtr(ptr: Long) = new Series(ptr)
297+
}

core/src/main/scala/org/polars/scala/polars/api/types/DataTypes.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ case object DoubleType extends BasicDataType
3131

3232
case object DateType extends BasicDataType
3333

34-
case class DateTimeType(precision: TimeUnit, timezone: ZoneId) extends DataType
34+
// todo: validate the timeunit and timezone and re-enable this later
35+
//case class DateTimeType(precision: TimeUnit, timezone: ZoneId) extends DataType
36+
37+
case object DateTimeType extends BasicDataType
3538

3639
case class ListType(tpe: DataType) extends DataType {
3740
override def simpleName: String = "list"

core/src/main/scala/org/polars/scala/polars/api/types/Schema.scala

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -54,22 +54,24 @@ class Schema private (private[polars] val json: String) {
5454

5555
// For DateTime Type
5656
case (name, node, _ @JsonNodeType.OBJECT) if node.has("Datetime") =>
57-
val dtNode = node.get("Datetime")
58-
59-
val (tu, tz) = dtNode.iterator().asScala.map(_.textValue()).toSeq match {
60-
case Seq(null, null) =>
61-
(TimeUnit.MICROSECONDS, ZoneId.of("UTC"))
62-
case Seq(null, tz) if tz.nonEmpty =>
63-
(TimeUnit.MICROSECONDS, ZoneId.of(tz))
64-
case Seq(tu, null) if tu.nonEmpty =>
65-
(TimeUnit.valueOf(tu.toUpperCase(Locale.ROOT)), ZoneId.of("UTC"))
66-
case Seq(tu, tz) if tu.nonEmpty && tz.nonEmpty =>
67-
(TimeUnit.valueOf(tu.toUpperCase(Locale.ROOT)), ZoneId.of(tz))
68-
case _ =>
69-
(TimeUnit.MICROSECONDS, ZoneId.of("UTC"))
70-
}
71-
72-
Field(name, DateTimeType(tu, tz))
57+
// todo: validate the timeunit and timezone and re-enable this later
58+
59+
// val dtNode = node.get("Datetime")
60+
//
61+
// val (tu, tz) = dtNode.iterator().asScala.map(_.textValue()).toSeq match {
62+
// case Seq(null, null) =>
63+
// (TimeUnit.MICROSECONDS, ZoneId.of("UTC"))
64+
// case Seq(null, tz) if tz.nonEmpty =>
65+
// (TimeUnit.MICROSECONDS, ZoneId.of(tz))
66+
// case Seq(tu, null) if tu.nonEmpty =>
67+
// (TimeUnit.valueOf(tu.toUpperCase(Locale.ROOT)), ZoneId.of("UTC"))
68+
// case Seq(tu, tz) if tu.nonEmpty && tz.nonEmpty =>
69+
// (TimeUnit.valueOf(tu.toUpperCase(Locale.ROOT)), ZoneId.of(tz))
70+
// case _ =>
71+
// (TimeUnit.MICROSECONDS, ZoneId.of("UTC"))
72+
// }
73+
74+
Field(name, DateTimeType)
7375

7476
// For (Nested) List Type
7577
case (name, node, _ @JsonNodeType.OBJECT) if node.has("List") =>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package org.polars.scala.polars.internal.jni
2+
3+
private[polars] object series extends Natively {
4+
5+
@native def show(ptr: Long): Unit
6+
7+
@native def new_str_series(name: String, data: Array[String]): Long
8+
9+
@native def new_int_series(name: String, data: Array[Int]): Long
10+
11+
@native def new_float_series(name: String, data: Array[Float]): Long
12+
13+
@native def new_double_series(name: String, data: Array[Double]): Long
14+
15+
@native def new_long_series(name: String, data: Array[Long]): Long
16+
17+
@native def new_boolean_series(name: String, data: Array[Boolean]): Long
18+
19+
@native def new_date_series(name: String, data: Array[String]): Long
20+
21+
@native def new_datetime_series(name: String, data: Array[String]): Long
22+
23+
@native def new_list_series(name: String, ptrs: Array[Long]): Long
24+
25+
}

0 commit comments

Comments
 (0)