Skip to content

Commit bec19d9

Browse files
committed
Merge branch 'master' into aggregators
2 parents 87165ba + ccbcd19 commit bec19d9

File tree

26 files changed

+872
-314
lines changed

26 files changed

+872
-314
lines changed

core/api/core.api

+2
Original file line numberDiff line numberDiff line change
@@ -5404,6 +5404,8 @@ public final class org/jetbrains/kotlinx/dataframe/impl/api/SchemaKt {
54045404
public final class org/jetbrains/kotlinx/dataframe/impl/api/ToDataFrameKt {
54055405
public static final fun convertToDataFrame (Ljava/lang/Iterable;Lkotlin/reflect/KClass;Ljava/util/List;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;I)Lorg/jetbrains/kotlinx/dataframe/DataFrame;
54065406
public static final fun createDataFrameImpl (Ljava/lang/Iterable;Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/kotlinx/dataframe/DataFrame;
5407+
public static final fun getHasProperties (Lkotlin/reflect/KClass;)Z
5408+
public static final fun isValueType (Lkotlin/reflect/KClass;)Z
54075409
}
54085410

54095411
public final class org/jetbrains/kotlinx/dataframe/impl/api/ToSequenceKt {

core/build.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ tasks.withType<ShadowJar> {
185185
exclude(dependency("org.jetbrains.kotlinx:kotlinx-datetime-jvm:.*"))
186186
exclude(dependency("commons-io:commons-io:.*"))
187187
exclude(dependency("commons-io:commons-csv:.*"))
188+
exclude(dependency("org.apache.commons:commons-csv:.*"))
188189
exclude(dependency("org.slf4j:slf4j-api:.*"))
189190
exclude(dependency("io.github.microutils:kotlin-logging-jvm:.*"))
190191
exclude(dependency("org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:.*"))

core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/DataFrame.kt

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import org.jetbrains.kotlinx.dataframe.aggregation.Aggregatable
44
import org.jetbrains.kotlinx.dataframe.aggregation.AggregateGroupedBody
55
import org.jetbrains.kotlinx.dataframe.annotations.AccessApiOverload
66
import org.jetbrains.kotlinx.dataframe.annotations.HasSchema
7+
import org.jetbrains.kotlinx.dataframe.annotations.Interpretable
8+
import org.jetbrains.kotlinx.dataframe.annotations.Refine
79
import org.jetbrains.kotlinx.dataframe.api.ColumnsSelectionDsl
810
import org.jetbrains.kotlinx.dataframe.api.add
911
import org.jetbrains.kotlinx.dataframe.api.cast
@@ -71,6 +73,8 @@ public interface DataFrame<out T> :
7173

7274
// endregion
7375

76+
@Refine
77+
@Interpretable("AggregateRow")
7478
public fun <R> aggregate(body: AggregateGroupedBody<T, R>): DataRow<T>
7579

7680
// region get columns

core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/DataColumnType.kt

+16-13
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import org.jetbrains.kotlinx.dataframe.columns.ColumnGroup
77
import org.jetbrains.kotlinx.dataframe.columns.ColumnKind
88
import org.jetbrains.kotlinx.dataframe.columns.FrameColumn
99
import org.jetbrains.kotlinx.dataframe.columns.ValueColumn
10-
import org.jetbrains.kotlinx.dataframe.impl.isNothing
11-
import org.jetbrains.kotlinx.dataframe.impl.projectTo
1210
import org.jetbrains.kotlinx.dataframe.type
1311
import org.jetbrains.kotlinx.dataframe.typeClass
1412
import org.jetbrains.kotlinx.dataframe.util.IS_COMPARABLE
@@ -21,8 +19,11 @@ import kotlin.contracts.contract
2119
import kotlin.reflect.KClass
2220
import kotlin.reflect.KType
2321
import kotlin.reflect.KTypeProjection
22+
import kotlin.reflect.KVariance
23+
import kotlin.reflect.full.createType
2424
import kotlin.reflect.full.isSubclassOf
2525
import kotlin.reflect.full.isSubtypeOf
26+
import kotlin.reflect.full.withNullability
2627
import kotlin.reflect.typeOf
2728

2829
public fun AnyCol.isColumnGroup(): Boolean {
@@ -53,14 +54,13 @@ public fun AnyCol.isBigNumber(): Boolean = isSubtypeOf<BigInteger?>() || isSubty
5354

5455
public fun AnyCol.isList(): Boolean = typeClass == List::class
5556

56-
/** Returns `true` if [this] column is inter-comparable, i.e.
57+
/** Returns `true` if [this] column is intra-comparable, i.e.
5758
* its values can be compared with each other and thus ordered.
5859
*
5960
* If true, operations like [`min()`][AnyCol.min], [`max()`][AnyCol.max], [`median()`][AnyCol.median], etc.
6061
* will work.
6162
*
62-
* Technically, this means the values' common type is a subtype of [Comparable] with
63-
* the type argument not being [Nothing]. */
63+
* Technically, this means the values' common type `T(?)` is a subtype of [Comparable]`<in T>(?)` */
6464
@Deprecated(
6565
message = IS_COMPARABLE,
6666
replaceWith = ReplaceWith(IS_COMPARABLE_REPLACE, IS_INTER_COMPARABLE_IMPORT),
@@ -69,21 +69,24 @@ public fun AnyCol.isList(): Boolean = typeClass == List::class
6969
public fun AnyCol.isComparable(): Boolean = valuesAreComparable()
7070

7171
/**
72-
* Returns `true` if [this] column is inter-comparable, i.e.
72+
* Returns `true` if [this] column is intra-comparable, i.e.
7373
* its values can be compared with each other and thus ordered.
7474
*
7575
* If true, operations like [`min()`][AnyCol.min], [`max()`][AnyCol.max], [`median()`][AnyCol.median], etc.
7676
* will work.
7777
*
78-
* Technically, this means the values' common type is a subtype of [Comparable] with
79-
* the type argument not being [Nothing].
78+
* Technically, this means the values' common type `T(?)` is a subtype of [Comparable]`<in T>(?)`
8079
*/
8180
public fun AnyCol.valuesAreComparable(): Boolean =
82-
isSubtypeOf<Comparable<*>?>() &&
83-
type().projectTo(Comparable::class).arguments[0].let {
84-
it != KTypeProjection.STAR &&
85-
it.type?.isNothing != true
86-
}
81+
isValueColumn() &&
82+
isSubtypeOf(
83+
Comparable::class.createType(
84+
arguments = listOf(
85+
KTypeProjection(KVariance.IN, type().withNullability(false)),
86+
),
87+
nullable = hasNulls(),
88+
),
89+
)
8790

8891
@PublishedApi
8992
internal fun AnyCol.isPrimitive(): Boolean = typeClass.isPrimitive()

core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/toDataFrame.kt

+11-94
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import org.jetbrains.kotlinx.dataframe.annotations.Refine
1212
import org.jetbrains.kotlinx.dataframe.columns.ColumnPath
1313
import org.jetbrains.kotlinx.dataframe.impl.ColumnNameGenerator
1414
import org.jetbrains.kotlinx.dataframe.impl.api.createDataFrameImpl
15+
import org.jetbrains.kotlinx.dataframe.impl.api.hasProperties
16+
import org.jetbrains.kotlinx.dataframe.impl.api.isValueType
1517
import org.jetbrains.kotlinx.dataframe.impl.asList
1618
import org.jetbrains.kotlinx.dataframe.impl.columnName
1719
import org.jetbrains.kotlinx.dataframe.impl.columns.createColumnGuessingType
@@ -26,7 +28,15 @@ import kotlin.reflect.KProperty
2628
@Interpretable("toDataFrameDefault")
2729
public inline fun <reified T> Iterable<T>.toDataFrame(): DataFrame<T> =
2830
toDataFrame {
29-
properties()
31+
// check if type is value: primitives, primitive arrays, datetime types etc.,
32+
// or has no properties
33+
if (T::class.isValueType || !T::class.hasProperties) {
34+
// create a single `value` column
35+
ValueProperty<T>::value from { it }
36+
} else {
37+
// otherwise creates columns based on properties
38+
properties()
39+
}
3040
}
3141

3242
@Refine
@@ -217,99 +227,6 @@ public inline fun <reified T> Iterable<T>.toDataFrame(columnName: String): DataF
217227

218228
// region toDataFrame overloads for built-in types
219229

220-
/*
221-
Without overloads Iterable<String>.toDataFrame produces unexpected result
222-
223-
224-
```
225-
val string = listOf("aaa", "aa", null)
226-
string.toDataFrame()
227-
```
228-
=>
229-
length
230-
0 3
231-
1 2
232-
2 null
233-
*/
234-
235-
@JvmName("toDataFrameByte")
236-
public inline fun <reified B : Byte?> Iterable<B>.toDataFrame(): DataFrame<ValueProperty<B>> =
237-
toDataFrame {
238-
ValueProperty<B>::value from { it }
239-
}.cast()
240-
241-
@JvmName("toDataFrameShort")
242-
public inline fun <reified S : Short?> Iterable<S>.toDataFrame(): DataFrame<ValueProperty<S>> =
243-
toDataFrame {
244-
ValueProperty<S>::value from { it }
245-
}.cast()
246-
247-
@JvmName("toDataFrameInt")
248-
public inline fun <reified I : Int?> Iterable<I>.toDataFrame(): DataFrame<ValueProperty<I>> =
249-
toDataFrame {
250-
ValueProperty<I>::value from { it }
251-
}.cast()
252-
253-
@JvmName("toDataFrameLong")
254-
public inline fun <reified L : Long?> Iterable<L>.toDataFrame(): DataFrame<ValueProperty<L>> =
255-
toDataFrame {
256-
ValueProperty<L>::value from { it }
257-
}.cast()
258-
259-
@JvmName("toDataFrameString")
260-
public inline fun <reified S : String?> Iterable<S>.toDataFrame(): DataFrame<ValueProperty<S>> =
261-
toDataFrame {
262-
ValueProperty<S>::value from { it }
263-
}.cast()
264-
265-
@JvmName("toDataFrameChar")
266-
public inline fun <reified C : Char?> Iterable<C>.toDataFrame(): DataFrame<ValueProperty<C>> =
267-
toDataFrame {
268-
ValueProperty<C>::value from { it }
269-
}.cast()
270-
271-
@JvmName("toDataFrameBoolean")
272-
public inline fun <reified B : Boolean?> Iterable<B>.toDataFrame(): DataFrame<ValueProperty<B>> =
273-
toDataFrame {
274-
ValueProperty<B>::value from { it }
275-
}.cast()
276-
277-
@JvmName("toDataFrameFloat")
278-
public inline fun <reified F : Float?> Iterable<F>.toDataFrame(): DataFrame<ValueProperty<F>> =
279-
toDataFrame {
280-
ValueProperty<F>::value from { it }
281-
}.cast()
282-
283-
@JvmName("toDataFrameDouble")
284-
public inline fun <reified D : Double?> Iterable<D>.toDataFrame(): DataFrame<ValueProperty<D>> =
285-
toDataFrame {
286-
ValueProperty<D>::value from { it }
287-
}.cast()
288-
289-
@JvmName("toDataFrameUByte")
290-
public inline fun <reified U : UByte?> Iterable<U>.toDataFrame(): DataFrame<ValueProperty<U>> =
291-
toDataFrame {
292-
ValueProperty<U>::value from { it }
293-
}.cast()
294-
295-
@JvmName("toDataFrameUShort")
296-
public inline fun <reified U : UShort?> Iterable<U>.toDataFrame(): DataFrame<ValueProperty<U>> =
297-
toDataFrame {
298-
ValueProperty<U>::value from { it }
299-
}.cast()
300-
301-
@JvmName("toDataFrameUInt")
302-
public inline fun <reified U : UInt?> Iterable<U>.toDataFrame(): DataFrame<ValueProperty<U>> =
303-
toDataFrame {
304-
ValueProperty<U>::value from { it }
305-
}.cast()
306-
307-
@JvmName("toDataFrameULong")
308-
public inline fun <reified U : ULong?> Iterable<U>.toDataFrame(): DataFrame<ValueProperty<U>> =
309-
toDataFrame {
310-
ValueProperty<U>::value from { it }
311-
}.cast()
312-
313230
@DataSchema
314231
public interface ValueProperty<T> {
315232
public val value: T

core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/toDataFrame.kt

+31-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ import org.jetbrains.kotlinx.dataframe.impl.projectUpTo
2323
import org.jetbrains.kotlinx.dataframe.impl.schema.sortWithConstructor
2424
import java.lang.reflect.InvocationTargetException
2525
import java.lang.reflect.Method
26-
import java.time.temporal.Temporal
26+
import java.time.temporal.TemporalAccessor
27+
import java.time.temporal.TemporalAmount
2728
import kotlin.reflect.KCallable
2829
import kotlin.reflect.KClass
2930
import kotlin.reflect.KProperty
@@ -37,23 +38,51 @@ import kotlin.reflect.jvm.isAccessible
3738
import kotlin.reflect.jvm.javaField
3839
import kotlin.reflect.typeOf
3940

41+
// non-standard value types (not supertypes, but exact types)
4042
private val valueTypes = setOf(
43+
Any::class,
44+
Unit::class,
45+
Char::class,
46+
UByte::class,
47+
UShort::class,
48+
UInt::class,
49+
ULong::class,
4150
String::class,
4251
Boolean::class,
4352
kotlin.time.Duration::class,
4453
kotlinx.datetime.LocalDate::class,
4554
kotlinx.datetime.LocalDateTime::class,
4655
kotlinx.datetime.Instant::class,
56+
kotlinx.datetime.TimeZone::class,
57+
kotlinx.datetime.DateTimePeriod::class,
58+
kotlinx.datetime.DateTimeUnit::class,
4759
)
4860

61+
/**
62+
* Checks if `KClass` is a value type (number, datetime, string, etc.)
63+
* Should be aligned with `ConeKotlinType.isValueType()` in
64+
* plugins/kotlin-dataframe/src/org/jetbrains/kotlinx/dataframe/plugin/impl/api/toDataFrame.kt
65+
*/
66+
@PublishedApi
4967
internal val KClass<*>.isValueType: Boolean
5068
get() =
5169
this in valueTypes ||
5270
this.isSubclassOf(Number::class) ||
5371
this.isSubclassOf(Enum::class) ||
54-
this.isSubclassOf(Temporal::class) ||
72+
// all java datetime types
73+
this.isSubclassOf(TemporalAccessor::class) ||
74+
this.isSubclassOf(TemporalAmount::class) ||
5575
this.isArray
5676

77+
/**
78+
* Checks if `KClass` has public properties / getter functions (for pojo-like classes).
79+
*/
80+
@PublishedApi
81+
internal val KClass<*>.hasProperties: Boolean
82+
get() = this.memberProperties.any { it.visibility == KVisibility.PUBLIC } ||
83+
// check pojo-like classes
84+
this.memberFunctions.any { it.visibility == KVisibility.PUBLIC && it.isGetterLike() }
85+
5786
internal class CreateDataFrameDslImpl<T>(
5887
override val source: Iterable<T>,
5988
private val clazz: KClass<*>,

0 commit comments

Comments
 (0)