polars-df 0.15.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +274 -0
- data/Cargo.lock +1465 -867
- data/Cargo.toml +3 -0
- data/LICENSE.txt +2 -2
- data/README.md +87 -37
- data/ext/polars/Cargo.toml +47 -16
- data/ext/polars/src/c_api/allocator.rs +7 -0
- data/ext/polars/src/c_api/mod.rs +1 -0
- data/ext/polars/src/catalog/mod.rs +1 -0
- data/ext/polars/src/catalog/unity.rs +470 -0
- data/ext/polars/src/conversion/any_value.rs +99 -84
- data/ext/polars/src/conversion/categorical.rs +30 -0
- data/ext/polars/src/conversion/chunked_array.rs +71 -62
- data/ext/polars/src/conversion/datetime.rs +63 -0
- data/ext/polars/src/conversion/mod.rs +796 -312
- data/ext/polars/src/dataframe/construction.rs +6 -18
- data/ext/polars/src/dataframe/export.rs +30 -39
- data/ext/polars/src/dataframe/general.rs +294 -362
- data/ext/polars/src/dataframe/io.rs +33 -150
- data/ext/polars/src/dataframe/map.rs +175 -0
- data/ext/polars/src/dataframe/mod.rs +37 -5
- data/ext/polars/src/dataframe/serde.rs +23 -8
- data/ext/polars/src/error.rs +44 -7
- data/ext/polars/src/exceptions.rs +21 -8
- data/ext/polars/src/expr/array.rs +86 -22
- data/ext/polars/src/expr/binary.rs +50 -1
- data/ext/polars/src/expr/bitwise.rs +39 -0
- data/ext/polars/src/expr/categorical.rs +20 -0
- data/ext/polars/src/expr/datatype.rs +51 -0
- data/ext/polars/src/expr/datetime.rs +99 -41
- data/ext/polars/src/expr/extension.rs +12 -0
- data/ext/polars/src/expr/general.rs +252 -128
- data/ext/polars/src/expr/list.rs +56 -60
- data/ext/polars/src/expr/meta.rs +30 -35
- data/ext/polars/src/expr/mod.rs +28 -6
- data/ext/polars/src/expr/name.rs +29 -14
- data/ext/polars/src/expr/rolling.rs +111 -3
- data/ext/polars/src/expr/selector.rs +219 -0
- data/ext/polars/src/expr/serde.rs +28 -0
- data/ext/polars/src/expr/string.rs +118 -20
- data/ext/polars/src/expr/struct.rs +14 -1
- data/ext/polars/src/file.rs +194 -86
- data/ext/polars/src/functions/aggregation.rs +13 -12
- data/ext/polars/src/functions/business.rs +2 -3
- data/ext/polars/src/functions/eager.rs +3 -2
- data/ext/polars/src/functions/io.rs +90 -18
- data/ext/polars/src/functions/lazy.rs +267 -118
- data/ext/polars/src/functions/meta.rs +8 -7
- data/ext/polars/src/functions/misc.rs +1 -1
- data/ext/polars/src/functions/mod.rs +2 -1
- data/ext/polars/src/functions/range.rs +88 -31
- data/ext/polars/src/functions/strings.rs +6 -0
- data/ext/polars/src/functions/utils.rs +8 -0
- data/ext/polars/src/interop/arrow/mod.rs +52 -1
- data/ext/polars/src/interop/arrow/{to_ruby.rs → to_rb.rs} +37 -7
- data/ext/polars/src/interop/arrow/to_rust.rs +43 -0
- data/ext/polars/src/interop/numo/to_numo_df.rs +1 -1
- data/ext/polars/src/interop/numo/to_numo_series.rs +72 -50
- data/ext/polars/src/io/cloud_options.rs +107 -0
- data/ext/polars/src/io/mod.rs +4 -0
- data/ext/polars/src/io/scan_options.rs +113 -0
- data/ext/polars/src/io/sink_options.rs +46 -0
- data/ext/polars/src/io/sink_output.rs +21 -0
- data/ext/polars/src/lazyframe/exitable.rs +39 -0
- data/ext/polars/src/lazyframe/general.rs +846 -368
- data/ext/polars/src/lazyframe/mod.rs +58 -5
- data/ext/polars/src/lazyframe/optflags.rs +59 -0
- data/ext/polars/src/lazyframe/serde.rs +36 -4
- data/ext/polars/src/lazyframe/sink.rs +46 -0
- data/ext/polars/src/lazygroupby.rs +38 -9
- data/ext/polars/src/lib.rs +574 -165
- data/ext/polars/src/map/lazy.rs +44 -74
- data/ext/polars/src/map/mod.rs +18 -254
- data/ext/polars/src/map/series.rs +241 -1087
- data/ext/polars/src/on_startup.rs +192 -9
- data/ext/polars/src/prelude.rs +1 -0
- data/ext/polars/src/rb_modules.rs +10 -57
- data/ext/polars/src/ruby/exceptions.rs +26 -0
- data/ext/polars/src/ruby/gvl.rs +104 -0
- data/ext/polars/src/ruby/lazy.rs +46 -0
- data/ext/polars/src/ruby/mod.rs +11 -0
- data/ext/polars/src/ruby/numo.rs +52 -0
- data/ext/polars/src/ruby/plan_callback.rs +198 -0
- data/ext/polars/src/ruby/rb_modules.rs +16 -0
- data/ext/polars/src/ruby/ruby_convert_registry.rs +51 -0
- data/ext/polars/src/ruby/ruby_function.rs +11 -0
- data/ext/polars/src/ruby/ruby_udf.rs +164 -0
- data/ext/polars/src/ruby/thread.rs +65 -0
- data/ext/polars/src/ruby/utils.rs +39 -0
- data/ext/polars/src/series/aggregation.rs +116 -91
- data/ext/polars/src/series/arithmetic.rs +16 -22
- data/ext/polars/src/series/comparison.rs +101 -222
- data/ext/polars/src/series/construction.rs +80 -70
- data/ext/polars/src/series/export.rs +98 -56
- data/ext/polars/src/series/general.rs +323 -440
- data/ext/polars/src/series/import.rs +22 -5
- data/ext/polars/src/series/map.rs +103 -0
- data/ext/polars/src/series/mod.rs +57 -15
- data/ext/polars/src/series/scatter.rs +139 -82
- data/ext/polars/src/sql.rs +16 -9
- data/ext/polars/src/testing/frame.rs +31 -0
- data/ext/polars/src/testing/mod.rs +5 -0
- data/ext/polars/src/testing/series.rs +31 -0
- data/ext/polars/src/timeout.rs +105 -0
- data/ext/polars/src/utils.rs +105 -4
- data/lib/polars/array_expr.rb +500 -22
- data/lib/polars/array_name_space.rb +384 -10
- data/lib/polars/batched_csv_reader.rb +48 -66
- data/lib/polars/binary_expr.rb +217 -0
- data/lib/polars/binary_name_space.rb +155 -1
- data/lib/polars/cat_expr.rb +224 -0
- data/lib/polars/cat_name_space.rb +132 -32
- data/lib/polars/catalog/unity/catalog_info.rb +20 -0
- data/lib/polars/catalog/unity/column_info.rb +31 -0
- data/lib/polars/catalog/unity/namespace_info.rb +21 -0
- data/lib/polars/catalog/unity/table_info.rb +50 -0
- data/lib/polars/catalog.rb +448 -0
- data/lib/polars/collect_batches.rb +22 -0
- data/lib/polars/config.rb +3 -3
- data/lib/polars/convert.rb +201 -36
- data/lib/polars/data_frame.rb +2851 -1017
- data/lib/polars/data_frame_plot.rb +173 -0
- data/lib/polars/data_type_expr.rb +52 -0
- data/lib/polars/data_type_group.rb +6 -0
- data/lib/polars/data_types.rb +118 -18
- data/lib/polars/date_time_expr.rb +426 -84
- data/lib/polars/date_time_name_space.rb +384 -111
- data/lib/polars/dynamic_group_by.rb +102 -10
- data/lib/polars/exceptions.rb +50 -5
- data/lib/polars/expr.rb +2159 -915
- data/lib/polars/extension_expr.rb +39 -0
- data/lib/polars/extension_name_space.rb +39 -0
- data/lib/polars/functions/aggregation/horizontal.rb +11 -6
- data/lib/polars/functions/aggregation/vertical.rb +2 -3
- data/lib/polars/functions/as_datatype.rb +290 -8
- data/lib/polars/functions/business.rb +95 -0
- data/lib/polars/functions/col.rb +6 -5
- data/lib/polars/functions/datatype.rb +62 -0
- data/lib/polars/functions/eager.rb +426 -24
- data/lib/polars/functions/escape_regex.rb +21 -0
- data/lib/polars/functions/lazy.rb +813 -195
- data/lib/polars/functions/lit.rb +21 -10
- data/lib/polars/functions/range/int_range.rb +74 -2
- data/lib/polars/functions/range/linear_space.rb +195 -0
- data/lib/polars/functions/range/time_range.rb +1 -1
- data/lib/polars/functions/repeat.rb +7 -12
- data/lib/polars/functions/whenthen.rb +2 -2
- data/lib/polars/group_by.rb +188 -58
- data/lib/polars/iceberg_dataset.rb +108 -0
- data/lib/polars/in_process_query.rb +37 -0
- data/lib/polars/io/cloud.rb +18 -0
- data/lib/polars/io/csv.rb +336 -128
- data/lib/polars/io/database.rb +19 -4
- data/lib/polars/io/delta.rb +134 -0
- data/lib/polars/io/iceberg.rb +34 -0
- data/lib/polars/io/ipc.rb +63 -63
- data/lib/polars/io/json.rb +16 -0
- data/lib/polars/io/lines.rb +172 -0
- data/lib/polars/io/ndjson.rb +176 -20
- data/lib/polars/io/parquet.rb +173 -95
- data/lib/polars/io/scan_options.rb +55 -0
- data/lib/polars/io/sink_options.rb +27 -0
- data/lib/polars/io/utils.rb +17 -0
- data/lib/polars/lazy_frame.rb +3017 -622
- data/lib/polars/lazy_group_by.rb +436 -2
- data/lib/polars/list_expr.rb +551 -59
- data/lib/polars/list_name_space.rb +465 -51
- data/lib/polars/meta_expr.rb +146 -24
- data/lib/polars/name_expr.rb +87 -2
- data/lib/polars/query_opt_flags.rb +264 -0
- data/lib/polars/rolling_group_by.rb +90 -5
- data/lib/polars/scan_cast_options.rb +86 -0
- data/lib/polars/schema.rb +128 -0
- data/lib/polars/selector.rb +245 -0
- data/lib/polars/selectors.rb +1048 -201
- data/lib/polars/series.rb +2522 -774
- data/lib/polars/series_plot.rb +72 -0
- data/lib/polars/slice.rb +1 -1
- data/lib/polars/sql_context.rb +13 -6
- data/lib/polars/string_cache.rb +19 -72
- data/lib/polars/string_expr.rb +561 -107
- data/lib/polars/string_name_space.rb +781 -109
- data/lib/polars/struct_expr.rb +139 -18
- data/lib/polars/struct_name_space.rb +19 -1
- data/lib/polars/testing.rb +24 -273
- data/lib/polars/utils/constants.rb +2 -0
- data/lib/polars/utils/construction/data_frame.rb +410 -0
- data/lib/polars/utils/construction/series.rb +350 -0
- data/lib/polars/utils/construction/utils.rb +9 -0
- data/lib/polars/utils/convert.rb +18 -8
- data/lib/polars/utils/deprecation.rb +11 -0
- data/lib/polars/utils/parse.rb +62 -9
- data/lib/polars/utils/reduce_balanced.rb +43 -0
- data/lib/polars/utils/serde.rb +22 -0
- data/lib/polars/utils/unstable.rb +19 -0
- data/lib/polars/utils/various.rb +86 -1
- data/lib/polars/utils.rb +63 -48
- data/lib/polars/version.rb +1 -1
- data/lib/polars.rb +85 -2
- metadata +80 -28
- data/ext/polars/src/allocator.rs +0 -13
- data/ext/polars/src/batched_csv.rs +0 -138
- data/ext/polars/src/functions/string_cache.rs +0 -25
- data/ext/polars/src/map/dataframe.rs +0 -338
- data/lib/polars/plot.rb +0 -109
data/lib/polars/data_frame.rb
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
module Polars
|
|
2
2
|
# Two-dimensional data structure representing data as a table with rows and columns.
|
|
3
3
|
class DataFrame
|
|
4
|
-
include Plot
|
|
5
|
-
|
|
6
4
|
# @private
|
|
7
5
|
attr_accessor :_df
|
|
8
6
|
|
|
@@ -15,11 +13,11 @@ module Polars
|
|
|
15
13
|
# The schema of the resulting DataFrame. The schema may be declared in several
|
|
16
14
|
# ways:
|
|
17
15
|
#
|
|
18
|
-
# * As a hash of name:type pairs; if type is nil, it will be auto-inferred.
|
|
16
|
+
# * As a hash of \\\\{name:type} pairs; if type is nil, it will be auto-inferred.
|
|
19
17
|
# * As an array of column names; in this case types are automatically inferred.
|
|
20
|
-
# * As an array of (name,type) pairs; this is equivalent to the
|
|
18
|
+
# * As an array of (name,type) pairs; this is equivalent to the hash form.
|
|
21
19
|
#
|
|
22
|
-
# If you supply
|
|
20
|
+
# If you supply an array of column names that does not match the names in the
|
|
23
21
|
# underlying data, the names given here will overwrite them. The number
|
|
24
22
|
# of names given in the schema should match the underlying data dimensions.
|
|
25
23
|
#
|
|
@@ -43,29 +41,40 @@ module Polars
|
|
|
43
41
|
# @param infer_schema_length [Integer]
|
|
44
42
|
# The maximum number of rows to scan for schema inference. If set to `nil`, the
|
|
45
43
|
# full data may be scanned *(this can be slow)*. This parameter only applies if
|
|
46
|
-
# the input data is
|
|
44
|
+
# the input data is an array or generator of rows; other input is read as-is.
|
|
47
45
|
# @param nan_to_null [Boolean]
|
|
48
46
|
# If the data comes from one or more Numo arrays, can optionally convert input
|
|
49
47
|
# data NaN values to null instead. This is a no-op for all other input data.
|
|
50
|
-
def initialize(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
48
|
+
def initialize(
|
|
49
|
+
data = nil,
|
|
50
|
+
schema: nil,
|
|
51
|
+
schema_overrides: nil,
|
|
52
|
+
strict: true,
|
|
53
|
+
orient: nil,
|
|
54
|
+
infer_schema_length: N_INFER_DEFAULT,
|
|
55
|
+
nan_to_null: false,
|
|
56
|
+
height: nil
|
|
57
|
+
)
|
|
56
58
|
if defined?(ActiveRecord) && (data.is_a?(ActiveRecord::Relation) || data.is_a?(ActiveRecord::Result))
|
|
57
59
|
raise ArgumentError, "Use read_database instead"
|
|
58
60
|
end
|
|
59
61
|
|
|
62
|
+
if !height.nil?
|
|
63
|
+
msg = "the `height` parameter of `DataFrame` is considered unstable."
|
|
64
|
+
Utils.issue_unstable_warning(msg)
|
|
65
|
+
|
|
66
|
+
raise Todo
|
|
67
|
+
end
|
|
68
|
+
|
|
60
69
|
if data.nil?
|
|
61
|
-
self._df =
|
|
70
|
+
self._df = Utils.hash_to_rbdf({}, schema: schema, schema_overrides: schema_overrides)
|
|
62
71
|
elsif data.is_a?(Hash)
|
|
63
72
|
data = data.transform_keys { |v| v.is_a?(Symbol) ? v.to_s : v }
|
|
64
|
-
self._df =
|
|
73
|
+
self._df = Utils.hash_to_rbdf(data, schema: schema, schema_overrides: schema_overrides, strict: strict, nan_to_null: nan_to_null)
|
|
65
74
|
elsif data.is_a?(::Array)
|
|
66
|
-
self._df =
|
|
75
|
+
self._df = Utils.sequence_to_rbdf(data, schema: schema, schema_overrides: schema_overrides, strict: strict, orient: orient, infer_schema_length: infer_schema_length)
|
|
67
76
|
elsif data.is_a?(Series)
|
|
68
|
-
self._df =
|
|
77
|
+
self._df = Utils.series_to_rbdf(data, schema: schema, schema_overrides: schema_overrides, strict: strict)
|
|
69
78
|
elsif data.respond_to?(:arrow_c_stream)
|
|
70
79
|
# This uses the fact that RbSeries.from_arrow_c_stream will create a
|
|
71
80
|
# struct-typed Series. Then we unpack that to a DataFrame.
|
|
@@ -77,6 +86,43 @@ module Polars
|
|
|
77
86
|
end
|
|
78
87
|
end
|
|
79
88
|
|
|
89
|
+
# Read a serialized DataFrame from a file.
|
|
90
|
+
#
|
|
91
|
+
# @param source [Object]
|
|
92
|
+
# Path to a file or a file-like object (by file-like object, we refer to
|
|
93
|
+
# objects that have a `read` method, such as a file handler or `StringIO`).
|
|
94
|
+
#
|
|
95
|
+
# @return [DataFrame]
|
|
96
|
+
#
|
|
97
|
+
# @note
|
|
98
|
+
# Serialization is not stable across Polars versions: a LazyFrame serialized
|
|
99
|
+
# in one Polars version may not be deserializable in another Polars version.
|
|
100
|
+
#
|
|
101
|
+
# @example
|
|
102
|
+
# df = Polars::DataFrame.new({"a" => [1, 2, 3], "b" => [4.0, 5.0, 6.0]})
|
|
103
|
+
# bytes = df.serialize
|
|
104
|
+
# Polars::DataFrame.deserialize(StringIO.new(bytes))
|
|
105
|
+
# # =>
|
|
106
|
+
# # shape: (3, 2)
|
|
107
|
+
# # ┌─────┬─────┐
|
|
108
|
+
# # │ a ┆ b │
|
|
109
|
+
# # │ --- ┆ --- │
|
|
110
|
+
# # │ i64 ┆ f64 │
|
|
111
|
+
# # ╞═════╪═════╡
|
|
112
|
+
# # │ 1 ┆ 4.0 │
|
|
113
|
+
# # │ 2 ┆ 5.0 │
|
|
114
|
+
# # │ 3 ┆ 6.0 │
|
|
115
|
+
# # └─────┴─────┘
|
|
116
|
+
def self.deserialize(source)
|
|
117
|
+
if Utils.pathlike?(source)
|
|
118
|
+
source = Utils.normalize_filepath(source)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
deserializer = RbDataFrame.method(:deserialize_binary)
|
|
122
|
+
|
|
123
|
+
_from_rbdf(deserializer.(source))
|
|
124
|
+
end
|
|
125
|
+
|
|
80
126
|
# @private
|
|
81
127
|
def self._from_rbdf(rb_df)
|
|
82
128
|
df = DataFrame.allocate
|
|
@@ -84,6 +130,45 @@ module Polars
|
|
|
84
130
|
df
|
|
85
131
|
end
|
|
86
132
|
|
|
133
|
+
# Plot data.
|
|
134
|
+
#
|
|
135
|
+
# @return [Object]
|
|
136
|
+
def plot(x = nil, y = nil, type: nil, group: nil, stacked: nil)
|
|
137
|
+
plot = DataFramePlot.new(self)
|
|
138
|
+
return plot if x.nil? && y.nil?
|
|
139
|
+
|
|
140
|
+
raise ArgumentError, "Must specify columns" if x.nil? || y.nil?
|
|
141
|
+
type ||= begin
|
|
142
|
+
if self[x].dtype.numeric? && self[y].dtype.numeric?
|
|
143
|
+
"scatter"
|
|
144
|
+
elsif self[x].dtype == String && self[y].dtype.numeric?
|
|
145
|
+
"column"
|
|
146
|
+
elsif (self[x].dtype == Date || self[x].dtype == Datetime) && self[y].dtype.numeric?
|
|
147
|
+
"line"
|
|
148
|
+
else
|
|
149
|
+
raise "Cannot determine type. Use the type option."
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
case type
|
|
154
|
+
when "line"
|
|
155
|
+
plot.line(x, y, color: group)
|
|
156
|
+
when "area"
|
|
157
|
+
plot.area(x, y, color: group)
|
|
158
|
+
when "pie"
|
|
159
|
+
raise ArgumentError, "Cannot use group option with pie chart" unless group.nil?
|
|
160
|
+
plot.pie(x, y)
|
|
161
|
+
when "column"
|
|
162
|
+
plot.column(x, y, color: group, stacked: stacked)
|
|
163
|
+
when "bar"
|
|
164
|
+
plot.bar(x, y, color: group, stacked: stacked)
|
|
165
|
+
when "scatter"
|
|
166
|
+
plot.scatter(x, y, color: group)
|
|
167
|
+
else
|
|
168
|
+
raise ArgumentError, "Invalid type: #{type}"
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
87
172
|
# Get the shape of the DataFrame.
|
|
88
173
|
#
|
|
89
174
|
# @return [Array]
|
|
@@ -212,9 +297,9 @@ module Polars
|
|
|
212
297
|
# }
|
|
213
298
|
# )
|
|
214
299
|
# df.schema
|
|
215
|
-
# # => {"foo"=>Polars::Int64, "bar"=>Polars::Float64, "ham"=>Polars::String}
|
|
300
|
+
# # => Polars::Schema({"foo"=>Polars::Int64, "bar"=>Polars::Float64, "ham"=>Polars::String})
|
|
216
301
|
def schema
|
|
217
|
-
columns.zip(dtypes).to_h
|
|
302
|
+
Schema.new(columns.zip(dtypes).to_h)
|
|
218
303
|
end
|
|
219
304
|
|
|
220
305
|
# Equal.
|
|
@@ -351,142 +436,243 @@ module Polars
|
|
|
351
436
|
# Returns subset of the DataFrame.
|
|
352
437
|
#
|
|
353
438
|
# @return [Object]
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
439
|
+
#
|
|
440
|
+
# @example
|
|
441
|
+
# df = Polars::DataFrame.new(
|
|
442
|
+
# {"a" => [1, 2, 3], "d" => [4, 5, 6], "c" => [1, 3, 2], "b" => [7, 8, 9]}
|
|
443
|
+
# )
|
|
444
|
+
# df[0]
|
|
445
|
+
# # =>
|
|
446
|
+
# # shape: (1, 4)
|
|
447
|
+
# # ┌─────┬─────┬─────┬─────┐
|
|
448
|
+
# # │ a ┆ d ┆ c ┆ b │
|
|
449
|
+
# # │ --- ┆ --- ┆ --- ┆ --- │
|
|
450
|
+
# # │ i64 ┆ i64 ┆ i64 ┆ i64 │
|
|
451
|
+
# # ╞═════╪═════╪═════╪═════╡
|
|
452
|
+
# # │ 1 ┆ 4 ┆ 1 ┆ 7 │
|
|
453
|
+
# # └─────┴─────┴─────┴─────┘
|
|
454
|
+
#
|
|
455
|
+
# @example
|
|
456
|
+
# df[0, "a"]
|
|
457
|
+
# # => 1
|
|
458
|
+
#
|
|
459
|
+
# @example
|
|
460
|
+
# df["a"]
|
|
461
|
+
# # =>
|
|
462
|
+
# # shape: (3,)
|
|
463
|
+
# # Series: 'a' [i64]
|
|
464
|
+
# # [
|
|
465
|
+
# # 1
|
|
466
|
+
# # 2
|
|
467
|
+
# # 3
|
|
468
|
+
# # ]
|
|
469
|
+
#
|
|
470
|
+
# @example
|
|
471
|
+
# df[0..1]
|
|
472
|
+
# # =>
|
|
473
|
+
# # shape: (2, 4)
|
|
474
|
+
# # ┌─────┬─────┬─────┬─────┐
|
|
475
|
+
# # │ a ┆ d ┆ c ┆ b │
|
|
476
|
+
# # │ --- ┆ --- ┆ --- ┆ --- │
|
|
477
|
+
# # │ i64 ┆ i64 ┆ i64 ┆ i64 │
|
|
478
|
+
# # ╞═════╪═════╪═════╪═════╡
|
|
479
|
+
# # │ 1 ┆ 4 ┆ 1 ┆ 7 │
|
|
480
|
+
# # │ 2 ┆ 5 ┆ 3 ┆ 8 │
|
|
481
|
+
# # └─────┴─────┴─────┴─────┘
|
|
482
|
+
#
|
|
483
|
+
# @example
|
|
484
|
+
# df[0..1, "a"]
|
|
485
|
+
# # =>
|
|
486
|
+
# # shape: (2,)
|
|
487
|
+
# # Series: 'a' [i64]
|
|
488
|
+
# # [
|
|
489
|
+
# # 1
|
|
490
|
+
# # 2
|
|
491
|
+
# # ]
|
|
492
|
+
#
|
|
493
|
+
# @example
|
|
494
|
+
# df[0..1, 0]
|
|
495
|
+
# # =>
|
|
496
|
+
# # shape: (2,)
|
|
497
|
+
# # Series: 'a' [i64]
|
|
498
|
+
# # [
|
|
499
|
+
# # 1
|
|
500
|
+
# # 2
|
|
501
|
+
# # ]
|
|
502
|
+
#
|
|
503
|
+
# @example
|
|
504
|
+
# df[[0, 1], [0, 1, 2]]
|
|
505
|
+
# # =>
|
|
506
|
+
# # shape: (2, 3)
|
|
507
|
+
# # ┌─────┬─────┬─────┐
|
|
508
|
+
# # │ a ┆ d ┆ c │
|
|
509
|
+
# # │ --- ┆ --- ┆ --- │
|
|
510
|
+
# # │ i64 ┆ i64 ┆ i64 │
|
|
511
|
+
# # ╞═════╪═════╪═════╡
|
|
512
|
+
# # │ 1 ┆ 4 ┆ 1 │
|
|
513
|
+
# # │ 2 ┆ 5 ┆ 3 │
|
|
514
|
+
# # └─────┴─────┴─────┘
|
|
515
|
+
#
|
|
516
|
+
# @example
|
|
517
|
+
# df[0..1, ["a", "c"]]
|
|
518
|
+
# # =>
|
|
519
|
+
# # shape: (2, 2)
|
|
520
|
+
# # ┌─────┬─────┐
|
|
521
|
+
# # │ a ┆ c │
|
|
522
|
+
# # │ --- ┆ --- │
|
|
523
|
+
# # │ i64 ┆ i64 │
|
|
524
|
+
# # ╞═════╪═════╡
|
|
525
|
+
# # │ 1 ┆ 1 │
|
|
526
|
+
# # │ 2 ┆ 3 │
|
|
527
|
+
# # └─────┴─────┘
|
|
528
|
+
#
|
|
529
|
+
# @example
|
|
530
|
+
# df[0.., 0..1]
|
|
531
|
+
# # =>
|
|
532
|
+
# # shape: (3, 2)
|
|
533
|
+
# # ┌─────┬─────┐
|
|
534
|
+
# # │ a ┆ d │
|
|
535
|
+
# # │ --- ┆ --- │
|
|
536
|
+
# # │ i64 ┆ i64 │
|
|
537
|
+
# # ╞═════╪═════╡
|
|
538
|
+
# # │ 1 ┆ 4 │
|
|
539
|
+
# # │ 2 ┆ 5 │
|
|
540
|
+
# # │ 3 ┆ 6 │
|
|
541
|
+
# # └─────┴─────┘
|
|
542
|
+
#
|
|
543
|
+
# @example
|
|
544
|
+
# df[0.., "a".."c"]
|
|
545
|
+
# # =>
|
|
546
|
+
# # shape: (3, 3)
|
|
547
|
+
# # ┌─────┬─────┬─────┐
|
|
548
|
+
# # │ a ┆ d ┆ c │
|
|
549
|
+
# # │ --- ┆ --- ┆ --- │
|
|
550
|
+
# # │ i64 ┆ i64 ┆ i64 │
|
|
551
|
+
# # ╞═════╪═════╪═════╡
|
|
552
|
+
# # │ 1 ┆ 4 ┆ 1 │
|
|
553
|
+
# # │ 2 ┆ 5 ┆ 3 │
|
|
554
|
+
# # │ 3 ┆ 6 ┆ 2 │
|
|
555
|
+
# # └─────┴─────┴─────┘
|
|
556
|
+
def [](*key)
|
|
557
|
+
get_df_item_by_key(self, key)
|
|
451
558
|
end
|
|
452
559
|
|
|
453
560
|
# Set item.
|
|
454
561
|
#
|
|
455
562
|
# @return [Object]
|
|
563
|
+
#
|
|
564
|
+
# @example `df[["a", "b"]] = value`:
|
|
565
|
+
# df = Polars::DataFrame.new({"a" => [1, 2, 3], "b" => [4, 5, 6]})
|
|
566
|
+
# df[["a", "b"]] = [[10, 40], [20, 50], [30, 60]]
|
|
567
|
+
# df
|
|
568
|
+
# # =>
|
|
569
|
+
# # shape: (3, 2)
|
|
570
|
+
# # ┌─────┬─────┐
|
|
571
|
+
# # │ a ┆ b │
|
|
572
|
+
# # │ --- ┆ --- │
|
|
573
|
+
# # │ i64 ┆ i64 │
|
|
574
|
+
# # ╞═════╪═════╡
|
|
575
|
+
# # │ 10 ┆ 40 │
|
|
576
|
+
# # │ 20 ┆ 50 │
|
|
577
|
+
# # │ 30 ┆ 60 │
|
|
578
|
+
# # └─────┴─────┘
|
|
579
|
+
#
|
|
580
|
+
# @example `df[row_idx, "a"] = value`:
|
|
581
|
+
# df[1, "a"] = 100
|
|
582
|
+
# df
|
|
583
|
+
# # =>
|
|
584
|
+
# # shape: (3, 2)
|
|
585
|
+
# # ┌─────┬─────┐
|
|
586
|
+
# # │ a ┆ b │
|
|
587
|
+
# # │ --- ┆ --- │
|
|
588
|
+
# # │ i64 ┆ i64 │
|
|
589
|
+
# # ╞═════╪═════╡
|
|
590
|
+
# # │ 10 ┆ 40 │
|
|
591
|
+
# # │ 100 ┆ 50 │
|
|
592
|
+
# # │ 30 ┆ 60 │
|
|
593
|
+
# # └─────┴─────┘
|
|
594
|
+
#
|
|
595
|
+
# @example `df[row_idx, col_idx] = value`:
|
|
596
|
+
# df[0, 1] = 30
|
|
597
|
+
# df
|
|
598
|
+
# # =>
|
|
599
|
+
# # shape: (3, 2)
|
|
600
|
+
# # ┌─────┬─────┐
|
|
601
|
+
# # │ a ┆ b │
|
|
602
|
+
# # │ --- ┆ --- │
|
|
603
|
+
# # │ i64 ┆ i64 │
|
|
604
|
+
# # ╞═════╪═════╡
|
|
605
|
+
# # │ 10 ┆ 30 │
|
|
606
|
+
# # │ 100 ┆ 50 │
|
|
607
|
+
# # │ 30 ┆ 60 │
|
|
608
|
+
# # └─────┴─────┘
|
|
456
609
|
def []=(*key, value)
|
|
457
|
-
if key.length
|
|
458
|
-
key = key.first
|
|
459
|
-
elsif key.length != 2
|
|
610
|
+
if key.empty? || key.length > 2
|
|
460
611
|
raise ArgumentError, "wrong number of arguments (given #{key.length + 1}, expected 2..3)"
|
|
461
612
|
end
|
|
462
613
|
|
|
463
|
-
if Utils.strlike?(key)
|
|
614
|
+
if key.length == 1 && Utils.strlike?(key[0])
|
|
615
|
+
key = key[0]
|
|
616
|
+
|
|
464
617
|
if value.is_a?(::Array) || (defined?(Numo::NArray) && value.is_a?(Numo::NArray))
|
|
465
618
|
value = Series.new(value)
|
|
466
619
|
elsif !value.is_a?(Series)
|
|
467
620
|
value = Polars.lit(value)
|
|
468
621
|
end
|
|
469
|
-
self._df =
|
|
470
|
-
|
|
622
|
+
self._df = with_columns(value.alias(key.to_s))._df
|
|
623
|
+
|
|
624
|
+
# df[["C", "D"]]
|
|
625
|
+
elsif key.length == 1 && key[0].is_a?(::Array)
|
|
626
|
+
key = key[0]
|
|
627
|
+
|
|
628
|
+
if !value.is_a?(::Array) || !value.all? { |v| v.is_a?(::Array) }
|
|
629
|
+
msg = "can only set multiple columns with 2D matrix"
|
|
630
|
+
raise ArgumentError, msg
|
|
631
|
+
end
|
|
632
|
+
if value.any? { |v| v.size != key.length }
|
|
633
|
+
msg = "matrix columns should be equal to list used to determine column names"
|
|
634
|
+
raise ArgumentError, msg
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
columns = []
|
|
638
|
+
key.each_with_index do |name, i|
|
|
639
|
+
columns << Series.new(name, value.map { |v| v[i] })
|
|
640
|
+
end
|
|
641
|
+
self._df = with_columns(columns)._df
|
|
642
|
+
|
|
643
|
+
# df[a, b]
|
|
644
|
+
else
|
|
471
645
|
row_selection, col_selection = key
|
|
472
646
|
|
|
647
|
+
if (row_selection.is_a?(Series) && row_selection.dtype == Boolean) || Utils.is_bool_sequence(row_selection)
|
|
648
|
+
msg = (
|
|
649
|
+
"not allowed to set DataFrame by boolean mask in the row position" +
|
|
650
|
+
"\n\nConsider using `DataFrame.with_columns`."
|
|
651
|
+
)
|
|
652
|
+
raise TypeError, msg
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
# get series column selection
|
|
473
656
|
if Utils.strlike?(col_selection)
|
|
474
657
|
s = self[col_selection]
|
|
475
658
|
elsif col_selection.is_a?(Integer)
|
|
476
|
-
|
|
659
|
+
s = self[0.., col_selection]
|
|
477
660
|
else
|
|
478
|
-
|
|
661
|
+
msg = "unexpected column selection #{col_selection.inspect}"
|
|
662
|
+
raise TypeError, msg
|
|
479
663
|
end
|
|
480
664
|
|
|
665
|
+
# dispatch to []= of Series to do modification
|
|
481
666
|
s[row_selection] = value
|
|
482
667
|
|
|
668
|
+
# now find the location to place series
|
|
669
|
+
# df[idx]
|
|
483
670
|
if col_selection.is_a?(Integer)
|
|
484
671
|
replace_column(col_selection, s)
|
|
672
|
+
# df["foo"]
|
|
485
673
|
elsif Utils.strlike?(col_selection)
|
|
486
|
-
|
|
674
|
+
_replace(col_selection.to_s, s)
|
|
487
675
|
end
|
|
488
|
-
else
|
|
489
|
-
raise Todo
|
|
490
676
|
end
|
|
491
677
|
end
|
|
492
678
|
|
|
@@ -495,22 +681,94 @@ module Polars
|
|
|
495
681
|
_df.arrow_c_stream
|
|
496
682
|
end
|
|
497
683
|
|
|
498
|
-
#
|
|
684
|
+
# Get an ordered mapping of column names to their data type.
|
|
685
|
+
#
|
|
686
|
+
# @return [Schema]
|
|
687
|
+
#
|
|
688
|
+
# @note
|
|
689
|
+
# This method is included to facilitate writing code that is generic for both
|
|
690
|
+
# DataFrame and LazyFrame.
|
|
691
|
+
#
|
|
692
|
+
# @example Determine the schema.
|
|
693
|
+
# df = Polars::DataFrame.new(
|
|
694
|
+
# {
|
|
695
|
+
# "foo" => [1, 2, 3],
|
|
696
|
+
# "bar" => [6.0, 7.0, 8.0],
|
|
697
|
+
# "ham" => ["a", "b", "c"]
|
|
698
|
+
# }
|
|
699
|
+
# )
|
|
700
|
+
# df.collect_schema
|
|
701
|
+
# # => Polars::Schema({"foo"=>Polars::Int64, "bar"=>Polars::Float64, "ham"=>Polars::String})
|
|
702
|
+
#
|
|
703
|
+
# @example Access various properties of the schema using the `Schema` object.
|
|
704
|
+
# schema = df.collect_schema
|
|
705
|
+
# schema["bar"]
|
|
706
|
+
# # => Polars::Float64
|
|
707
|
+
#
|
|
708
|
+
# @example
|
|
709
|
+
# schema.names
|
|
710
|
+
# # => ["foo", "bar", "ham"]
|
|
711
|
+
#
|
|
712
|
+
# @example
|
|
713
|
+
# schema.dtypes
|
|
714
|
+
# # => [Polars::Int64, Polars::Float64, Polars::String]
|
|
715
|
+
#
|
|
716
|
+
# @example
|
|
717
|
+
# schema.length
|
|
718
|
+
# # => 3
|
|
719
|
+
def collect_schema
|
|
720
|
+
Schema.new(columns.zip(dtypes), check_dtypes: false)
|
|
721
|
+
end
|
|
722
|
+
|
|
723
|
+
# Return the DataFrame as a scalar, or return the element at the given row/column.
|
|
499
724
|
#
|
|
500
|
-
#
|
|
725
|
+
# @param row [Integer]
|
|
726
|
+
# Optional row index.
|
|
727
|
+
# @param column [Integer, String]
|
|
728
|
+
# Optional column index or name.
|
|
501
729
|
#
|
|
502
730
|
# @return [Object]
|
|
503
731
|
#
|
|
732
|
+
# @note
|
|
733
|
+
# If row/col not provided, this is equivalent to `df[0,0]`, with a check that
|
|
734
|
+
# the shape is (1,1). With row/col, this is equivalent to `df[row,col]`.
|
|
735
|
+
#
|
|
504
736
|
# @example
|
|
505
737
|
# df = Polars::DataFrame.new({"a" => [1, 2, 3], "b" => [4, 5, 6]})
|
|
506
|
-
#
|
|
507
|
-
# result.item
|
|
738
|
+
# df.select((Polars.col("a") * Polars.col("b")).sum).item
|
|
508
739
|
# # => 32
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
740
|
+
#
|
|
741
|
+
# @example
|
|
742
|
+
# df.item(1, 1)
|
|
743
|
+
# # => 5
|
|
744
|
+
#
|
|
745
|
+
# @example
|
|
746
|
+
# df.item(2, "b")
|
|
747
|
+
# # => 6
|
|
748
|
+
def item(row = nil, column = nil)
|
|
749
|
+
if row.nil? && column.nil?
|
|
750
|
+
if shape != [1, 1]
|
|
751
|
+
msg = (
|
|
752
|
+
"can only call `.item()` if the dataframe is of shape (1, 1)," +
|
|
753
|
+
" or if explicit row/col values are provided;" +
|
|
754
|
+
" frame has shape #{shape.inspect}"
|
|
755
|
+
)
|
|
756
|
+
raise ArgumentError, msg
|
|
757
|
+
end
|
|
758
|
+
return _df.to_series(0).get_index(0)
|
|
759
|
+
|
|
760
|
+
elsif row.nil? || column.nil?
|
|
761
|
+
msg = "cannot call `.item()` with only one of `row` or `column`"
|
|
762
|
+
raise ArgumentError, msg
|
|
512
763
|
end
|
|
513
|
-
|
|
764
|
+
|
|
765
|
+
s =
|
|
766
|
+
if column.is_a?(Integer)
|
|
767
|
+
_df.to_series(column)
|
|
768
|
+
else
|
|
769
|
+
_df.get_column(column)
|
|
770
|
+
end
|
|
771
|
+
s.get_index_signed(row)
|
|
514
772
|
end
|
|
515
773
|
|
|
516
774
|
# no to_arrow
|
|
@@ -526,9 +784,7 @@ module Polars
|
|
|
526
784
|
end
|
|
527
785
|
end
|
|
528
786
|
|
|
529
|
-
# Convert every row to a
|
|
530
|
-
#
|
|
531
|
-
# Note that this is slow.
|
|
787
|
+
# Convert every row to a hash.
|
|
532
788
|
#
|
|
533
789
|
# @return [Array]
|
|
534
790
|
#
|
|
@@ -538,13 +794,8 @@ module Polars
|
|
|
538
794
|
# # =>
|
|
539
795
|
# # [{"foo"=>1, "bar"=>4}, {"foo"=>2, "bar"=>5}, {"foo"=>3, "bar"=>6}]
|
|
540
796
|
def to_hashes
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
height.times.map do |i|
|
|
545
|
-
names.zip(rbdf.row_tuple(i)).to_h
|
|
546
|
-
end
|
|
547
|
-
end
|
|
797
|
+
rows(named: true)
|
|
798
|
+
end
|
|
548
799
|
|
|
549
800
|
# Convert DataFrame to a 2D Numo array.
|
|
550
801
|
#
|
|
@@ -597,17 +848,51 @@ module Polars
|
|
|
597
848
|
if index < 0
|
|
598
849
|
index = columns.length + index
|
|
599
850
|
end
|
|
600
|
-
Utils.wrap_s(_df.
|
|
851
|
+
Utils.wrap_s(_df.to_series(index))
|
|
852
|
+
end
|
|
853
|
+
|
|
854
|
+
# Serialize this DataFrame to a file or string.
|
|
855
|
+
#
|
|
856
|
+
# @param file [Object]
|
|
857
|
+
# File path or writable file-like object to which the result will be written.
|
|
858
|
+
# If set to `nil` (default), the output is returned as a string instead.
|
|
859
|
+
#
|
|
860
|
+
# @return [Object]
|
|
861
|
+
#
|
|
862
|
+
# @note
|
|
863
|
+
# Serialization is not stable across Polars versions: a LazyFrame serialized
|
|
864
|
+
# in one Polars version may not be deserializable in another Polars version.
|
|
865
|
+
#
|
|
866
|
+
# @example
|
|
867
|
+
# df = Polars::DataFrame.new(
|
|
868
|
+
# {
|
|
869
|
+
# "foo" => [1, 2, 3],
|
|
870
|
+
# "bar" => [6, 7, 8]
|
|
871
|
+
# }
|
|
872
|
+
# )
|
|
873
|
+
# bytes = df.serialize
|
|
874
|
+
# Polars::DataFrame.deserialize(StringIO.new(bytes))
|
|
875
|
+
# # =>
|
|
876
|
+
# # shape: (3, 2)
|
|
877
|
+
# # ┌─────┬─────┐
|
|
878
|
+
# # │ foo ┆ bar │
|
|
879
|
+
# # │ --- ┆ --- │
|
|
880
|
+
# # │ i64 ┆ i64 │
|
|
881
|
+
# # ╞═════╪═════╡
|
|
882
|
+
# # │ 1 ┆ 6 │
|
|
883
|
+
# # │ 2 ┆ 7 │
|
|
884
|
+
# # │ 3 ┆ 8 │
|
|
885
|
+
# # └─────┴─────┘
|
|
886
|
+
def serialize(file = nil)
|
|
887
|
+
serializer = _df.method(:serialize_binary)
|
|
888
|
+
|
|
889
|
+
Utils.serialize_polars_object(serializer, file)
|
|
601
890
|
end
|
|
602
891
|
|
|
603
892
|
# Serialize to JSON representation.
|
|
604
893
|
#
|
|
605
894
|
# @param file [String]
|
|
606
895
|
# File path to which the result should be written.
|
|
607
|
-
# @param pretty [Boolean]
|
|
608
|
-
# Pretty serialize json.
|
|
609
|
-
# @param row_oriented [Boolean]
|
|
610
|
-
# Write to row oriented json. This is slower, but more common.
|
|
611
896
|
#
|
|
612
897
|
# @return [nil]
|
|
613
898
|
#
|
|
@@ -619,16 +904,8 @@ module Polars
|
|
|
619
904
|
# }
|
|
620
905
|
# )
|
|
621
906
|
# df.write_json
|
|
622
|
-
# # => "{\"columns\":[{\"name\":\"foo\",\"datatype\":\"Int64\",\"bit_settings\":\"\",\"values\":[1,2,3]},{\"name\":\"bar\",\"datatype\":\"Int64\",\"bit_settings\":\"\",\"values\":[6,7,8]}]}"
|
|
623
|
-
#
|
|
624
|
-
# @example
|
|
625
|
-
# df.write_json(row_oriented: true)
|
|
626
907
|
# # => "[{\"foo\":1,\"bar\":6},{\"foo\":2,\"bar\":7},{\"foo\":3,\"bar\":8}]"
|
|
627
|
-
def write_json(
|
|
628
|
-
file = nil,
|
|
629
|
-
pretty: false,
|
|
630
|
-
row_oriented: false
|
|
631
|
-
)
|
|
908
|
+
def write_json(file = nil)
|
|
632
909
|
if Utils.pathlike?(file)
|
|
633
910
|
file = Utils.normalize_filepath(file)
|
|
634
911
|
end
|
|
@@ -636,7 +913,7 @@ module Polars
|
|
|
636
913
|
if file.nil? || to_string_io
|
|
637
914
|
buf = StringIO.new
|
|
638
915
|
buf.set_encoding(Encoding::BINARY)
|
|
639
|
-
_df.write_json(buf
|
|
916
|
+
_df.write_json(buf)
|
|
640
917
|
json_bytes = buf.string
|
|
641
918
|
|
|
642
919
|
json_str = json_bytes.force_encoding(Encoding::UTF_8)
|
|
@@ -646,7 +923,7 @@ module Polars
|
|
|
646
923
|
return json_str
|
|
647
924
|
end
|
|
648
925
|
else
|
|
649
|
-
_df.write_json(file
|
|
926
|
+
_df.write_json(file)
|
|
650
927
|
end
|
|
651
928
|
nil
|
|
652
929
|
end
|
|
@@ -667,26 +944,38 @@ module Polars
|
|
|
667
944
|
# )
|
|
668
945
|
# df.write_ndjson
|
|
669
946
|
# # => "{\"foo\":1,\"bar\":6}\n{\"foo\":2,\"bar\":7}\n{\"foo\":3,\"bar\":8}\n"
|
|
670
|
-
def write_ndjson(
|
|
671
|
-
|
|
672
|
-
|
|
947
|
+
def write_ndjson(
|
|
948
|
+
file = nil,
|
|
949
|
+
compression: "uncompressed",
|
|
950
|
+
compression_level: nil,
|
|
951
|
+
check_extension: true
|
|
952
|
+
)
|
|
953
|
+
should_return_buffer = false
|
|
954
|
+
if file.nil?
|
|
955
|
+
target = StringIO.new
|
|
956
|
+
target.set_encoding(Encoding::BINARY)
|
|
957
|
+
should_return_buffer = true
|
|
958
|
+
elsif Utils.pathlike?(file)
|
|
959
|
+
target = Utils.normalize_filepath(file)
|
|
960
|
+
else
|
|
961
|
+
target = file
|
|
673
962
|
end
|
|
674
|
-
to_string_io = !file.nil? && file.is_a?(StringIO)
|
|
675
|
-
if file.nil? || to_string_io
|
|
676
|
-
buf = StringIO.new
|
|
677
|
-
buf.set_encoding(Encoding::BINARY)
|
|
678
|
-
_df.write_ndjson(buf)
|
|
679
|
-
json_bytes = buf.string
|
|
680
963
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
964
|
+
engine = "in-memory"
|
|
965
|
+
|
|
966
|
+
lazy.sink_ndjson(
|
|
967
|
+
target,
|
|
968
|
+
compression: compression,
|
|
969
|
+
compression_level: compression_level,
|
|
970
|
+
check_extension: check_extension,
|
|
971
|
+
optimizations: QueryOptFlags._eager,
|
|
972
|
+
engine: engine
|
|
973
|
+
)
|
|
974
|
+
|
|
975
|
+
if should_return_buffer
|
|
976
|
+
return target.string.force_encoding(Encoding::UTF_8)
|
|
689
977
|
end
|
|
978
|
+
|
|
690
979
|
nil
|
|
691
980
|
end
|
|
692
981
|
|
|
@@ -695,11 +984,11 @@ module Polars
|
|
|
695
984
|
# @param file [String, nil]
|
|
696
985
|
# File path to which the result should be written. If set to `nil`
|
|
697
986
|
# (default), the output is returned as a string instead.
|
|
698
|
-
# @param
|
|
987
|
+
# @param include_header [Boolean]
|
|
699
988
|
# Whether to include header in the CSV output.
|
|
700
|
-
# @param
|
|
989
|
+
# @param separator [String]
|
|
701
990
|
# Separate CSV fields with this symbol.
|
|
702
|
-
# @param
|
|
991
|
+
# @param quote_char [String]
|
|
703
992
|
# Byte to use as quoting character.
|
|
704
993
|
# @param batch_size [Integer]
|
|
705
994
|
# Number of rows that will be processed per thread.
|
|
@@ -718,8 +1007,8 @@ module Polars
|
|
|
718
1007
|
# [chrono](https://docs.rs/chrono/latest/chrono/format/strftime/index.html)
|
|
719
1008
|
# Rust crate.
|
|
720
1009
|
# @param float_precision [Integer, nil]
|
|
721
|
-
# Number of decimal places to write, applied to both
|
|
722
|
-
#
|
|
1010
|
+
# Number of decimal places to write, applied to both `Float32` and
|
|
1011
|
+
# `Float64` datatypes.
|
|
723
1012
|
# @param null_value [String, nil]
|
|
724
1013
|
# A string representing null values (defaulting to the empty string).
|
|
725
1014
|
#
|
|
@@ -736,61 +1025,71 @@ module Polars
|
|
|
736
1025
|
# df.write_csv("file.csv")
|
|
737
1026
|
def write_csv(
|
|
738
1027
|
file = nil,
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
1028
|
+
include_bom: false,
|
|
1029
|
+
compression: "uncompressed",
|
|
1030
|
+
compression_level: nil,
|
|
1031
|
+
check_extension: true,
|
|
1032
|
+
include_header: true,
|
|
1033
|
+
separator: ",",
|
|
1034
|
+
line_terminator: "\n",
|
|
1035
|
+
quote_char: '"',
|
|
743
1036
|
batch_size: 1024,
|
|
744
1037
|
datetime_format: nil,
|
|
745
1038
|
date_format: nil,
|
|
746
1039
|
time_format: nil,
|
|
1040
|
+
float_scientific: nil,
|
|
747
1041
|
float_precision: nil,
|
|
748
|
-
|
|
1042
|
+
decimal_comma: false,
|
|
1043
|
+
null_value: nil,
|
|
1044
|
+
quote_style: nil,
|
|
1045
|
+
storage_options: nil,
|
|
1046
|
+
credential_provider: "auto",
|
|
1047
|
+
retries: nil
|
|
749
1048
|
)
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
if
|
|
753
|
-
raise ArgumentError, "only single byte separator is allowed"
|
|
754
|
-
elsif quote.length > 1
|
|
755
|
-
raise ArgumentError, "only single byte quote char is allowed"
|
|
756
|
-
elsif null_value == ""
|
|
1049
|
+
Utils._check_arg_is_1byte("separator", separator, false)
|
|
1050
|
+
Utils._check_arg_is_1byte("quote_char", quote_char, true)
|
|
1051
|
+
if null_value == ""
|
|
757
1052
|
null_value = nil
|
|
758
1053
|
end
|
|
759
1054
|
|
|
1055
|
+
should_return_buffer = false
|
|
760
1056
|
if file.nil?
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
sep.ord,
|
|
767
|
-
quote.ord,
|
|
768
|
-
batch_size,
|
|
769
|
-
datetime_format,
|
|
770
|
-
date_format,
|
|
771
|
-
time_format,
|
|
772
|
-
float_precision,
|
|
773
|
-
null_value
|
|
774
|
-
)
|
|
775
|
-
return buffer.string.force_encoding(Encoding::UTF_8)
|
|
1057
|
+
target = StringIO.new
|
|
1058
|
+
target.set_encoding(Encoding::BINARY)
|
|
1059
|
+
should_return_buffer = true
|
|
1060
|
+
else
|
|
1061
|
+
target = file
|
|
776
1062
|
end
|
|
777
1063
|
|
|
778
|
-
|
|
779
|
-
|
|
1064
|
+
engine = "in-memory"
|
|
1065
|
+
|
|
1066
|
+
lazy.sink_csv(
|
|
1067
|
+
target,
|
|
1068
|
+
include_bom: include_bom,
|
|
1069
|
+
include_header: include_header,
|
|
1070
|
+
separator: separator,
|
|
1071
|
+
line_terminator: line_terminator,
|
|
1072
|
+
quote_char: quote_char,
|
|
1073
|
+
batch_size: batch_size,
|
|
1074
|
+
datetime_format: datetime_format,
|
|
1075
|
+
date_format: date_format,
|
|
1076
|
+
time_format: time_format,
|
|
1077
|
+
float_scientific: float_scientific,
|
|
1078
|
+
float_precision: float_precision,
|
|
1079
|
+
decimal_comma: decimal_comma,
|
|
1080
|
+
null_value: null_value,
|
|
1081
|
+
quote_style: quote_style,
|
|
1082
|
+
storage_options: storage_options,
|
|
1083
|
+
credential_provider: credential_provider,
|
|
1084
|
+
retries: retries,
|
|
1085
|
+
optimizations: QueryOptFlags._eager,
|
|
1086
|
+
engine: engine
|
|
1087
|
+
)
|
|
1088
|
+
|
|
1089
|
+
if should_return_buffer
|
|
1090
|
+
return target.string.force_encoding(Encoding::UTF_8)
|
|
780
1091
|
end
|
|
781
1092
|
|
|
782
|
-
_df.write_csv(
|
|
783
|
-
file,
|
|
784
|
-
include_header,
|
|
785
|
-
sep.ord,
|
|
786
|
-
quote.ord,
|
|
787
|
-
batch_size,
|
|
788
|
-
datetime_format,
|
|
789
|
-
date_format,
|
|
790
|
-
time_format,
|
|
791
|
-
float_precision,
|
|
792
|
-
null_value,
|
|
793
|
-
)
|
|
794
1093
|
nil
|
|
795
1094
|
end
|
|
796
1095
|
|
|
@@ -807,6 +1106,8 @@ module Polars
|
|
|
807
1106
|
# File path to which the file should be written.
|
|
808
1107
|
# @param compression ["uncompressed", "snappy", "deflate"]
|
|
809
1108
|
# Compression method. Defaults to "uncompressed".
|
|
1109
|
+
# @param name [String]
|
|
1110
|
+
# Schema name. Defaults to empty string.
|
|
810
1111
|
#
|
|
811
1112
|
# @return [nil]
|
|
812
1113
|
def write_avro(file, compression = "uncompressed", name: "")
|
|
@@ -829,28 +1130,59 @@ module Polars
|
|
|
829
1130
|
# File path to which the file should be written.
|
|
830
1131
|
# @param compression ["uncompressed", "lz4", "zstd"]
|
|
831
1132
|
# Compression method. Defaults to "uncompressed".
|
|
1133
|
+
# @param compat_level [Object]
|
|
1134
|
+
# Use a specific compatibility level
|
|
1135
|
+
# when exporting Polars' internal data structures.
|
|
1136
|
+
# @param storage_options [Hash]
|
|
1137
|
+
# Options that indicate how to connect to a cloud provider.
|
|
1138
|
+
#
|
|
1139
|
+
# The cloud providers currently supported are AWS, GCP, and Azure.
|
|
1140
|
+
# See supported keys here:
|
|
1141
|
+
#
|
|
1142
|
+
# * [aws](https://docs.rs/object_store/latest/object_store/aws/enum.AmazonS3ConfigKey.html)
|
|
1143
|
+
# * [gcp](https://docs.rs/object_store/latest/object_store/gcp/enum.GoogleConfigKey.html)
|
|
1144
|
+
# * [azure](https://docs.rs/object_store/latest/object_store/azure/enum.AzureConfigKey.html)
|
|
1145
|
+
# * Hugging Face (`hf://`): Accepts an API key under the `token` parameter: `{'token': '...'}`, or by setting the `HF_TOKEN` environment variable.
|
|
1146
|
+
#
|
|
1147
|
+
# If `storage_options` is not provided, Polars will try to infer the
|
|
1148
|
+
# information from environment variables.
|
|
1149
|
+
# @param credential_provider [Object]
|
|
1150
|
+
# Provide a function that can be called to provide cloud storage
|
|
1151
|
+
# credentials. The function is expected to return a hash of
|
|
1152
|
+
# credential keys along with an optional credential expiry time.
|
|
1153
|
+
# @param retries [Integer]
|
|
1154
|
+
# Number of retries if accessing a cloud instance fails.
|
|
832
1155
|
#
|
|
833
1156
|
# @return [nil]
|
|
834
|
-
def write_ipc(
|
|
1157
|
+
def write_ipc(
|
|
1158
|
+
file,
|
|
1159
|
+
compression: "uncompressed",
|
|
1160
|
+
compat_level: nil,
|
|
1161
|
+
record_batch_size: nil,
|
|
1162
|
+
storage_options: nil,
|
|
1163
|
+
credential_provider: "auto",
|
|
1164
|
+
retries: nil
|
|
1165
|
+
)
|
|
835
1166
|
return_bytes = file.nil?
|
|
836
|
-
if
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
file = Utils.normalize_filepath(file)
|
|
842
|
-
end
|
|
843
|
-
|
|
844
|
-
if compat_level.nil?
|
|
845
|
-
compat_level = true
|
|
846
|
-
end
|
|
847
|
-
|
|
848
|
-
if compression.nil?
|
|
849
|
-
compression = "uncompressed"
|
|
1167
|
+
if file.nil?
|
|
1168
|
+
target = StringIO.new
|
|
1169
|
+
target.set_encoding(Encoding::BINARY)
|
|
1170
|
+
else
|
|
1171
|
+
target = file
|
|
850
1172
|
end
|
|
851
1173
|
|
|
852
|
-
|
|
853
|
-
|
|
1174
|
+
lazy.sink_ipc(
|
|
1175
|
+
target,
|
|
1176
|
+
compression: compression,
|
|
1177
|
+
compat_level: compat_level,
|
|
1178
|
+
record_batch_size: record_batch_size,
|
|
1179
|
+
storage_options: storage_options,
|
|
1180
|
+
credential_provider: credential_provider,
|
|
1181
|
+
retries: retries,
|
|
1182
|
+
optimizations: QueryOptFlags._eager,
|
|
1183
|
+
engine: "streaming"
|
|
1184
|
+
)
|
|
1185
|
+
return_bytes ? target.string : nil
|
|
854
1186
|
end
|
|
855
1187
|
|
|
856
1188
|
# Write to Arrow IPC record batch stream.
|
|
@@ -859,9 +1191,12 @@ module Polars
|
|
|
859
1191
|
#
|
|
860
1192
|
# @param file [Object]
|
|
861
1193
|
# Path or writable file-like object to which the IPC record batch data will
|
|
862
|
-
# be written. If set to `
|
|
1194
|
+
# be written. If set to `nil`, the output is returned as a BytesIO object.
|
|
863
1195
|
# @param compression ['uncompressed', 'lz4', 'zstd']
|
|
864
1196
|
# Compression method. Defaults to "uncompressed".
|
|
1197
|
+
# @param compat_level [Object]
|
|
1198
|
+
# Use a specific compatibility level
|
|
1199
|
+
# when exporting Polars' internal data structures.
|
|
865
1200
|
#
|
|
866
1201
|
# @return [Object]
|
|
867
1202
|
#
|
|
@@ -927,9 +1262,17 @@ module Polars
|
|
|
927
1262
|
file,
|
|
928
1263
|
compression: "zstd",
|
|
929
1264
|
compression_level: nil,
|
|
930
|
-
statistics:
|
|
1265
|
+
statistics: true,
|
|
931
1266
|
row_group_size: nil,
|
|
932
|
-
data_page_size: nil
|
|
1267
|
+
data_page_size: nil,
|
|
1268
|
+
partition_by: nil,
|
|
1269
|
+
partition_chunk_size_bytes: 4_294_967_296,
|
|
1270
|
+
storage_options: nil,
|
|
1271
|
+
credential_provider: "auto",
|
|
1272
|
+
retries: nil,
|
|
1273
|
+
metadata: nil,
|
|
1274
|
+
arrow_schema: nil,
|
|
1275
|
+
mkdir: false
|
|
933
1276
|
)
|
|
934
1277
|
if compression.nil?
|
|
935
1278
|
compression = "uncompressed"
|
|
@@ -938,29 +1281,252 @@ module Polars
|
|
|
938
1281
|
file = Utils.normalize_filepath(file)
|
|
939
1282
|
end
|
|
940
1283
|
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
distinct_count: false,
|
|
946
|
-
null_count: true
|
|
947
|
-
}
|
|
948
|
-
elsif statistics == false
|
|
949
|
-
statistics = {}
|
|
950
|
-
elsif statistics == "full"
|
|
951
|
-
statistics = {
|
|
952
|
-
min: true,
|
|
953
|
-
max: true,
|
|
954
|
-
distinct_count: true,
|
|
955
|
-
null_count: true
|
|
956
|
-
}
|
|
1284
|
+
target = file
|
|
1285
|
+
engine = "streaming"
|
|
1286
|
+
if !partition_by.nil?
|
|
1287
|
+
raise Todo
|
|
957
1288
|
end
|
|
958
1289
|
|
|
959
|
-
|
|
960
|
-
|
|
1290
|
+
lazy.sink_parquet(
|
|
1291
|
+
target,
|
|
1292
|
+
compression: compression,
|
|
1293
|
+
compression_level: compression_level,
|
|
1294
|
+
statistics: statistics,
|
|
1295
|
+
row_group_size: row_group_size,
|
|
1296
|
+
data_page_size: data_page_size,
|
|
1297
|
+
storage_options: storage_options,
|
|
1298
|
+
credential_provider: credential_provider,
|
|
1299
|
+
retries: retries,
|
|
1300
|
+
metadata: metadata,
|
|
1301
|
+
arrow_schema: arrow_schema,
|
|
1302
|
+
engine: engine,
|
|
1303
|
+
mkdir: mkdir,
|
|
1304
|
+
optimizations: QueryOptFlags._eager
|
|
961
1305
|
)
|
|
962
1306
|
end
|
|
963
1307
|
|
|
1308
|
+
# Write the data in a Polars DataFrame to a database.
|
|
1309
|
+
#
|
|
1310
|
+
# @param table_name [String]
|
|
1311
|
+
# Schema-qualified name of the table to create or append to in the target
|
|
1312
|
+
# SQL database.
|
|
1313
|
+
# @param connection [Object]
|
|
1314
|
+
# An existing Active Record connection against the target database.
|
|
1315
|
+
# @param if_table_exists ['append', 'replace', 'fail']
|
|
1316
|
+
# The insert mode:
|
|
1317
|
+
#
|
|
1318
|
+
# * 'replace' will create a new database table, overwriting an existing one.
|
|
1319
|
+
# * 'append' will append to an existing table.
|
|
1320
|
+
# * 'fail' will fail if table already exists.
|
|
1321
|
+
#
|
|
1322
|
+
# @return [Integer]
|
|
1323
|
+
#
|
|
1324
|
+
# @note
|
|
1325
|
+
# This functionality is experimental. It may be changed at any point without it being considered a breaking change.
|
|
1326
|
+
def write_database(table_name, connection = nil, if_table_exists: "fail")
|
|
1327
|
+
if !defined?(ActiveRecord)
|
|
1328
|
+
raise Error, "Active Record not available"
|
|
1329
|
+
elsif ActiveRecord::VERSION::MAJOR < 7
|
|
1330
|
+
raise Error, "Requires Active Record 7+"
|
|
1331
|
+
end
|
|
1332
|
+
|
|
1333
|
+
valid_write_modes = ["append", "replace", "fail"]
|
|
1334
|
+
if !valid_write_modes.include?(if_table_exists)
|
|
1335
|
+
msg = "write_database `if_table_exists` must be one of #{valid_write_modes.inspect}, got #{if_table_exists.inspect}"
|
|
1336
|
+
raise ArgumentError, msg
|
|
1337
|
+
end
|
|
1338
|
+
|
|
1339
|
+
with_connection(connection) do |connection|
|
|
1340
|
+
table_exists = connection.table_exists?(table_name)
|
|
1341
|
+
if table_exists && if_table_exists == "fail"
|
|
1342
|
+
raise ArgumentError, "Table already exists"
|
|
1343
|
+
end
|
|
1344
|
+
|
|
1345
|
+
create_table = !table_exists || if_table_exists == "replace"
|
|
1346
|
+
maybe_transaction(connection, create_table) do
|
|
1347
|
+
if create_table
|
|
1348
|
+
mysql = connection.adapter_name.match?(/mysql|trilogy/i)
|
|
1349
|
+
force = if_table_exists == "replace"
|
|
1350
|
+
connection.create_table(table_name, id: false, force: force) do |t|
|
|
1351
|
+
schema.each do |c, dtype|
|
|
1352
|
+
options = {}
|
|
1353
|
+
column_type =
|
|
1354
|
+
case dtype
|
|
1355
|
+
when Binary
|
|
1356
|
+
:binary
|
|
1357
|
+
when Boolean
|
|
1358
|
+
:boolean
|
|
1359
|
+
when Date
|
|
1360
|
+
:date
|
|
1361
|
+
when Datetime
|
|
1362
|
+
:datetime
|
|
1363
|
+
when Decimal
|
|
1364
|
+
if mysql
|
|
1365
|
+
options[:precision] = dtype.precision || 65
|
|
1366
|
+
options[:scale] = dtype.scale || 30
|
|
1367
|
+
end
|
|
1368
|
+
:decimal
|
|
1369
|
+
when Float32
|
|
1370
|
+
options[:limit] = 24
|
|
1371
|
+
:float
|
|
1372
|
+
when Float64
|
|
1373
|
+
options[:limit] = 53
|
|
1374
|
+
:float
|
|
1375
|
+
when Int8
|
|
1376
|
+
options[:limit] = 1
|
|
1377
|
+
:integer
|
|
1378
|
+
when Int16
|
|
1379
|
+
options[:limit] = 2
|
|
1380
|
+
:integer
|
|
1381
|
+
when Int32
|
|
1382
|
+
options[:limit] = 4
|
|
1383
|
+
:integer
|
|
1384
|
+
when Int64
|
|
1385
|
+
options[:limit] = 8
|
|
1386
|
+
:integer
|
|
1387
|
+
when UInt8
|
|
1388
|
+
if mysql
|
|
1389
|
+
options[:limit] = 1
|
|
1390
|
+
options[:unsigned] = true
|
|
1391
|
+
else
|
|
1392
|
+
options[:limit] = 2
|
|
1393
|
+
end
|
|
1394
|
+
:integer
|
|
1395
|
+
when UInt16
|
|
1396
|
+
if mysql
|
|
1397
|
+
options[:limit] = 2
|
|
1398
|
+
options[:unsigned] = true
|
|
1399
|
+
else
|
|
1400
|
+
options[:limit] = 4
|
|
1401
|
+
end
|
|
1402
|
+
:integer
|
|
1403
|
+
when UInt32
|
|
1404
|
+
if mysql
|
|
1405
|
+
options[:limit] = 4
|
|
1406
|
+
options[:unsigned] = true
|
|
1407
|
+
else
|
|
1408
|
+
options[:limit] = 8
|
|
1409
|
+
end
|
|
1410
|
+
:integer
|
|
1411
|
+
when UInt64
|
|
1412
|
+
if mysql
|
|
1413
|
+
options[:limit] = 8
|
|
1414
|
+
options[:unsigned] = true
|
|
1415
|
+
:integer
|
|
1416
|
+
else
|
|
1417
|
+
options[:precision] = 20
|
|
1418
|
+
options[:scale] = 0
|
|
1419
|
+
:decimal
|
|
1420
|
+
end
|
|
1421
|
+
when String
|
|
1422
|
+
:text
|
|
1423
|
+
when Time
|
|
1424
|
+
:time
|
|
1425
|
+
else
|
|
1426
|
+
raise ArgumentError, "column type not supported yet: #{dtype}"
|
|
1427
|
+
end
|
|
1428
|
+
t.column c, column_type, **options
|
|
1429
|
+
end
|
|
1430
|
+
end
|
|
1431
|
+
end
|
|
1432
|
+
|
|
1433
|
+
quoted_table = connection.quote_table_name(table_name)
|
|
1434
|
+
quoted_columns = columns.map { |c| connection.quote_column_name(c) }
|
|
1435
|
+
rows = cast({Polars::UInt64 => Polars::String}).rows(named: false).map { |row| "(#{row.map { |v| connection.quote(v) }.join(", ")})" }
|
|
1436
|
+
connection.exec_update("INSERT INTO #{quoted_table} (#{quoted_columns.join(", ")}) VALUES #{rows.join(", ")}")
|
|
1437
|
+
end
|
|
1438
|
+
end
|
|
1439
|
+
end
|
|
1440
|
+
|
|
1441
|
+
# Write DataFrame to an Iceberg table.
|
|
1442
|
+
#
|
|
1443
|
+
# @note
|
|
1444
|
+
# This functionality is currently considered **unstable**. It may be
|
|
1445
|
+
# changed at any point without it being considered a breaking change.
|
|
1446
|
+
#
|
|
1447
|
+
# @param target [Object]
|
|
1448
|
+
# Name of the table or the Table object representing an Iceberg table.
|
|
1449
|
+
# @param mode ['append', 'overwrite']
|
|
1450
|
+
# How to handle existing data.
|
|
1451
|
+
#
|
|
1452
|
+
# - If 'append', will add new data.
|
|
1453
|
+
# - If 'overwrite', will replace table with new data.
|
|
1454
|
+
#
|
|
1455
|
+
# @return [nil]
|
|
1456
|
+
def write_iceberg(target, mode:)
|
|
1457
|
+
require "iceberg"
|
|
1458
|
+
|
|
1459
|
+
table =
|
|
1460
|
+
if target.is_a?(Iceberg::Table)
|
|
1461
|
+
target
|
|
1462
|
+
else
|
|
1463
|
+
raise Todo
|
|
1464
|
+
end
|
|
1465
|
+
|
|
1466
|
+
data = self
|
|
1467
|
+
|
|
1468
|
+
if mode == "append"
|
|
1469
|
+
table.append(data)
|
|
1470
|
+
else
|
|
1471
|
+
raise Todo
|
|
1472
|
+
end
|
|
1473
|
+
end
|
|
1474
|
+
|
|
1475
|
+
# Write DataFrame as delta table.
|
|
1476
|
+
#
|
|
1477
|
+
# @param target [Object]
|
|
1478
|
+
# URI of a table or a DeltaTable object.
|
|
1479
|
+
# @param mode ["error", "append", "overwrite", "ignore", "merge"]
|
|
1480
|
+
# How to handle existing data.
|
|
1481
|
+
# @param storage_options [Hash]
|
|
1482
|
+
# Extra options for the storage backends supported by `deltalake-rb`.
|
|
1483
|
+
# @param delta_write_options [Hash]
|
|
1484
|
+
# Additional keyword arguments while writing a Delta lake Table.
|
|
1485
|
+
# @param delta_merge_options [Hash]
|
|
1486
|
+
# Keyword arguments which are required to `MERGE` a Delta lake Table.
|
|
1487
|
+
#
|
|
1488
|
+
# @return [nil]
|
|
1489
|
+
def write_delta(
|
|
1490
|
+
target,
|
|
1491
|
+
mode: "error",
|
|
1492
|
+
storage_options: nil,
|
|
1493
|
+
delta_write_options: nil,
|
|
1494
|
+
delta_merge_options: nil
|
|
1495
|
+
)
|
|
1496
|
+
Polars.send(:_check_if_delta_available)
|
|
1497
|
+
|
|
1498
|
+
if Utils.pathlike?(target)
|
|
1499
|
+
target = Polars.send(:_resolve_delta_lake_uri, target.to_s, strict: false)
|
|
1500
|
+
end
|
|
1501
|
+
|
|
1502
|
+
data = self
|
|
1503
|
+
|
|
1504
|
+
if mode == "merge"
|
|
1505
|
+
if delta_merge_options.nil?
|
|
1506
|
+
msg = "You need to pass delta_merge_options with at least a given predicate for `MERGE` to work."
|
|
1507
|
+
raise ArgumentError, msg
|
|
1508
|
+
end
|
|
1509
|
+
if target.is_a?(::String)
|
|
1510
|
+
dt = DeltaLake::Table.new(target, storage_options: storage_options)
|
|
1511
|
+
else
|
|
1512
|
+
dt = target
|
|
1513
|
+
end
|
|
1514
|
+
|
|
1515
|
+
predicate = delta_merge_options.delete(:predicate)
|
|
1516
|
+
dt.merge(data, predicate, **delta_merge_options)
|
|
1517
|
+
else
|
|
1518
|
+
delta_write_options ||= {}
|
|
1519
|
+
|
|
1520
|
+
DeltaLake.write(
|
|
1521
|
+
target,
|
|
1522
|
+
data,
|
|
1523
|
+
mode: mode,
|
|
1524
|
+
storage_options: storage_options,
|
|
1525
|
+
**delta_write_options
|
|
1526
|
+
)
|
|
1527
|
+
end
|
|
1528
|
+
end
|
|
1529
|
+
|
|
964
1530
|
# Return an estimation of the total (heap) allocated size of the DataFrame.
|
|
965
1531
|
#
|
|
966
1532
|
# Estimated size is given in the specified unit (bytes by default).
|
|
@@ -988,7 +1554,7 @@ module Polars
|
|
|
988
1554
|
# "y" => 1_000_000.times.map { |v| v / 1000.0 },
|
|
989
1555
|
# "z" => 1_000_000.times.map(&:to_s)
|
|
990
1556
|
# },
|
|
991
|
-
#
|
|
1557
|
+
# schema: {"x" => Polars::UInt32, "y" => Polars::Float64, "z" => Polars::String}
|
|
992
1558
|
# )
|
|
993
1559
|
# df.estimated_size
|
|
994
1560
|
# # => 25888898
|
|
@@ -1120,14 +1686,14 @@ module Polars
|
|
|
1120
1686
|
# # │ 3 ┆ 8 ┆ c │
|
|
1121
1687
|
# # └───────┴─────┴─────┘
|
|
1122
1688
|
def rename(mapping, strict: true)
|
|
1123
|
-
lazy.rename(mapping, strict: strict).collect(
|
|
1689
|
+
lazy.rename(mapping, strict: strict).collect(optimizations: QueryOptFlags._eager)
|
|
1124
1690
|
end
|
|
1125
1691
|
|
|
1126
1692
|
# Insert a Series at a certain column index. This operation is in place.
|
|
1127
1693
|
#
|
|
1128
1694
|
# @param index [Integer]
|
|
1129
1695
|
# Column to insert the new `Series` column.
|
|
1130
|
-
# @param
|
|
1696
|
+
# @param column [Series]
|
|
1131
1697
|
# `Series` to insert.
|
|
1132
1698
|
#
|
|
1133
1699
|
# @return [DataFrame]
|
|
@@ -1170,19 +1736,22 @@ module Polars
|
|
|
1170
1736
|
# # │ 3 ┆ 10.0 ┆ false ┆ 20.5 │
|
|
1171
1737
|
# # │ 4 ┆ 13.0 ┆ true ┆ 0.0 │
|
|
1172
1738
|
# # └─────┴──────┴───────┴──────┘
|
|
1173
|
-
def insert_column(index,
|
|
1739
|
+
def insert_column(index, column)
|
|
1174
1740
|
if index < 0
|
|
1175
|
-
index =
|
|
1741
|
+
index = width + index
|
|
1176
1742
|
end
|
|
1177
|
-
_df.insert_column(index,
|
|
1743
|
+
_df.insert_column(index, column._s)
|
|
1178
1744
|
self
|
|
1179
1745
|
end
|
|
1180
|
-
alias_method :insert_at_idx, :insert_column
|
|
1181
1746
|
|
|
1182
1747
|
# Filter the rows in the DataFrame based on a predicate expression.
|
|
1183
1748
|
#
|
|
1184
|
-
# @param
|
|
1185
|
-
# Expression that
|
|
1749
|
+
# @param predicates [Array]
|
|
1750
|
+
# Expression(s) that evaluate to a boolean Series.
|
|
1751
|
+
# @param constraints [Hash]
|
|
1752
|
+
# Column filters; use `name = value` to filter columns by the supplied value.
|
|
1753
|
+
# Each constraint will behave the same as `Polars.col(name).eq(value)`, and
|
|
1754
|
+
# be implicitly joined with the other filter conditions using `&`.
|
|
1186
1755
|
#
|
|
1187
1756
|
# @return [DataFrame]
|
|
1188
1757
|
#
|
|
@@ -1217,86 +1786,307 @@ module Polars
|
|
|
1217
1786
|
# # ╞═════╪═════╪═════╡
|
|
1218
1787
|
# # │ 1 ┆ 6 ┆ a │
|
|
1219
1788
|
# # └─────┴─────┴─────┘
|
|
1220
|
-
def filter(
|
|
1221
|
-
lazy.filter(
|
|
1789
|
+
def filter(*predicates, **constraints)
|
|
1790
|
+
lazy.filter(*predicates, **constraints).collect(optimizations: QueryOptFlags._eager)
|
|
1222
1791
|
end
|
|
1223
1792
|
|
|
1224
|
-
#
|
|
1793
|
+
# Remove rows, dropping those that match the given predicate expression(s).
|
|
1794
|
+
#
|
|
1795
|
+
# The original order of the remaining rows is preserved.
|
|
1796
|
+
#
|
|
1797
|
+
# Rows where the filter predicate does not evaluate to true are retained
|
|
1798
|
+
# (this includes rows where the predicate evaluates as `null`).
|
|
1799
|
+
#
|
|
1800
|
+
# @param predicates [Array]
|
|
1801
|
+
# Expression that evaluates to a boolean Series.
|
|
1802
|
+
# @param constraints [Hash]
|
|
1803
|
+
# Column filters; use `name = value` to filter columns using the supplied
|
|
1804
|
+
# value. Each constraint behaves the same as `Polars.col(name).eq(value)`,
|
|
1805
|
+
# and is implicitly joined with the other filter conditions using `&`.
|
|
1225
1806
|
#
|
|
1226
1807
|
# @return [DataFrame]
|
|
1227
1808
|
#
|
|
1809
|
+
# @example Remove rows matching a condition:
|
|
1810
|
+
# df = Polars::DataFrame.new(
|
|
1811
|
+
# {
|
|
1812
|
+
# "foo" => [2, 3, nil, 4, 0],
|
|
1813
|
+
# "bar" => [5, 6, nil, nil, 0],
|
|
1814
|
+
# "ham" => ["a", "b", nil, "c", "d"]
|
|
1815
|
+
# }
|
|
1816
|
+
# )
|
|
1817
|
+
# df.remove(Polars.col("bar") >= 5)
|
|
1818
|
+
# # =>
|
|
1819
|
+
# # shape: (3, 3)
|
|
1820
|
+
# # ┌──────┬──────┬──────┐
|
|
1821
|
+
# # │ foo ┆ bar ┆ ham │
|
|
1822
|
+
# # │ --- ┆ --- ┆ --- │
|
|
1823
|
+
# # │ i64 ┆ i64 ┆ str │
|
|
1824
|
+
# # ╞══════╪══════╪══════╡
|
|
1825
|
+
# # │ null ┆ null ┆ null │
|
|
1826
|
+
# # │ 4 ┆ null ┆ c │
|
|
1827
|
+
# # │ 0 ┆ 0 ┆ d │
|
|
1828
|
+
# # └──────┴──────┴──────┘
|
|
1829
|
+
#
|
|
1830
|
+
# @example Discard rows based on multiple conditions, combined with and/or operators:
|
|
1831
|
+
# df.remove(
|
|
1832
|
+
# (Polars.col("foo") >= 0) & (Polars.col("bar") >= 0),
|
|
1833
|
+
# )
|
|
1834
|
+
# # =>
|
|
1835
|
+
# # shape: (2, 3)
|
|
1836
|
+
# # ┌──────┬──────┬──────┐
|
|
1837
|
+
# # │ foo ┆ bar ┆ ham │
|
|
1838
|
+
# # │ --- ┆ --- ┆ --- │
|
|
1839
|
+
# # │ i64 ┆ i64 ┆ str │
|
|
1840
|
+
# # ╞══════╪══════╪══════╡
|
|
1841
|
+
# # │ null ┆ null ┆ null │
|
|
1842
|
+
# # │ 4 ┆ null ┆ c │
|
|
1843
|
+
# # └──────┴──────┴──────┘
|
|
1844
|
+
#
|
|
1228
1845
|
# @example
|
|
1846
|
+
# df.remove(
|
|
1847
|
+
# (Polars.col("foo") >= 0) | (Polars.col("bar") >= 0),
|
|
1848
|
+
# )
|
|
1849
|
+
# # =>
|
|
1850
|
+
# # shape: (1, 3)
|
|
1851
|
+
# # ┌──────┬──────┬──────┐
|
|
1852
|
+
# # │ foo ┆ bar ┆ ham │
|
|
1853
|
+
# # │ --- ┆ --- ┆ --- │
|
|
1854
|
+
# # │ i64 ┆ i64 ┆ str │
|
|
1855
|
+
# # ╞══════╪══════╪══════╡
|
|
1856
|
+
# # │ null ┆ null ┆ null │
|
|
1857
|
+
# # └──────┴──────┴──────┘
|
|
1858
|
+
#
|
|
1859
|
+
# @example Provide multiple constraints using `*args` syntax:
|
|
1860
|
+
# df.remove(
|
|
1861
|
+
# Polars.col("ham").is_not_null,
|
|
1862
|
+
# Polars.col("bar") >= 0
|
|
1863
|
+
# )
|
|
1864
|
+
# # =>
|
|
1865
|
+
# # shape: (2, 3)
|
|
1866
|
+
# # ┌──────┬──────┬──────┐
|
|
1867
|
+
# # │ foo ┆ bar ┆ ham │
|
|
1868
|
+
# # │ --- ┆ --- ┆ --- │
|
|
1869
|
+
# # │ i64 ┆ i64 ┆ str │
|
|
1870
|
+
# # ╞══════╪══════╪══════╡
|
|
1871
|
+
# # │ null ┆ null ┆ null │
|
|
1872
|
+
# # │ 4 ┆ null ┆ c │
|
|
1873
|
+
# # └──────┴──────┴──────┘
|
|
1874
|
+
#
|
|
1875
|
+
# @example Provide constraints(s) using `**kwargs` syntax:
|
|
1876
|
+
# df.remove(foo: 0, bar: 0)
|
|
1877
|
+
# # =>
|
|
1878
|
+
# # shape: (4, 3)
|
|
1879
|
+
# # ┌──────┬──────┬──────┐
|
|
1880
|
+
# # │ foo ┆ bar ┆ ham │
|
|
1881
|
+
# # │ --- ┆ --- ┆ --- │
|
|
1882
|
+
# # │ i64 ┆ i64 ┆ str │
|
|
1883
|
+
# # ╞══════╪══════╪══════╡
|
|
1884
|
+
# # │ 2 ┆ 5 ┆ a │
|
|
1885
|
+
# # │ 3 ┆ 6 ┆ b │
|
|
1886
|
+
# # │ null ┆ null ┆ null │
|
|
1887
|
+
# # │ 4 ┆ null ┆ c │
|
|
1888
|
+
# # └──────┴──────┴──────┘
|
|
1889
|
+
#
|
|
1890
|
+
# @example Remove rows by comparing two columns against each other:
|
|
1891
|
+
# df.remove(
|
|
1892
|
+
# Polars.col("foo").ne_missing(Polars.col("bar"))
|
|
1893
|
+
# )
|
|
1894
|
+
# # =>
|
|
1895
|
+
# # shape: (2, 3)
|
|
1896
|
+
# # ┌──────┬──────┬──────┐
|
|
1897
|
+
# # │ foo ┆ bar ┆ ham │
|
|
1898
|
+
# # │ --- ┆ --- ┆ --- │
|
|
1899
|
+
# # │ i64 ┆ i64 ┆ str │
|
|
1900
|
+
# # ╞══════╪══════╪══════╡
|
|
1901
|
+
# # │ null ┆ null ┆ null │
|
|
1902
|
+
# # │ 0 ┆ 0 ┆ d │
|
|
1903
|
+
# # └──────┴──────┴──────┘
|
|
1904
|
+
def remove(
|
|
1905
|
+
*predicates,
|
|
1906
|
+
**constraints
|
|
1907
|
+
)
|
|
1908
|
+
lazy
|
|
1909
|
+
.remove(*predicates, **constraints)
|
|
1910
|
+
.collect(optimizations: QueryOptFlags._eager)
|
|
1911
|
+
end
|
|
1912
|
+
|
|
1913
|
+
# Return a dense preview of the DataFrame.
|
|
1914
|
+
#
|
|
1915
|
+
# The formatting shows one line per column so that wide dataframes display
|
|
1916
|
+
# cleanly. Each line shows the column name, the data type, and the first
|
|
1917
|
+
# few values.
|
|
1918
|
+
#
|
|
1919
|
+
# @param max_items_per_column [Integer]
|
|
1920
|
+
# Maximum number of items to show per column.
|
|
1921
|
+
# @param max_colname_length [Integer]
|
|
1922
|
+
# Maximum length of the displayed column names; values that exceed
|
|
1923
|
+
# this value are truncated with a trailing ellipsis.
|
|
1924
|
+
# @param return_type [nil, 'self', 'frame', 'string']
|
|
1925
|
+
# Modify the return format:
|
|
1926
|
+
#
|
|
1927
|
+
# - `nil` (default): Print the glimpse output to stdout, returning `nil`.
|
|
1928
|
+
# - `"self"`: Print the glimpse output to stdout, returning the *original* frame.
|
|
1929
|
+
# - `"frame"`: Return the glimpse output as a new DataFrame.
|
|
1930
|
+
# - `"string"`: Return the glimpse output as a string.
|
|
1931
|
+
#
|
|
1932
|
+
# @return [Object]
|
|
1933
|
+
#
|
|
1934
|
+
# @example Return the glimpse output as a DataFrame:
|
|
1229
1935
|
# df = Polars::DataFrame.new(
|
|
1230
1936
|
# {
|
|
1231
1937
|
# "a" => [1.0, 2.8, 3.0],
|
|
1232
1938
|
# "b" => [4, 5, nil],
|
|
1233
1939
|
# "c" => [true, false, true],
|
|
1234
1940
|
# "d" => [nil, "b", "c"],
|
|
1235
|
-
# "e" => ["usd", "eur", nil]
|
|
1941
|
+
# "e" => ["usd", "eur", nil],
|
|
1942
|
+
# "f" => [Date.new(2020, 1, 1), Date.new(2021, 1, 2), Date.new(2022, 1, 1)]
|
|
1236
1943
|
# }
|
|
1237
1944
|
# )
|
|
1238
|
-
# df.
|
|
1945
|
+
# df.glimpse(return_type: "frame")
|
|
1239
1946
|
# # =>
|
|
1240
|
-
# # shape: (
|
|
1241
|
-
# #
|
|
1242
|
-
# # │
|
|
1243
|
-
# # │ ---
|
|
1244
|
-
# # │ str
|
|
1245
|
-
# #
|
|
1246
|
-
# # │
|
|
1247
|
-
# # │
|
|
1248
|
-
# # │
|
|
1249
|
-
# # │
|
|
1250
|
-
# # │
|
|
1251
|
-
# # │
|
|
1252
|
-
# #
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1947
|
+
# # shape: (6, 3)
|
|
1948
|
+
# # ┌────────┬───────┬─────────────────────────────────┐
|
|
1949
|
+
# # │ column ┆ dtype ┆ values │
|
|
1950
|
+
# # │ --- ┆ --- ┆ --- │
|
|
1951
|
+
# # │ str ┆ str ┆ list[str] │
|
|
1952
|
+
# # ╞════════╪═══════╪═════════════════════════════════╡
|
|
1953
|
+
# # │ a ┆ f64 ┆ ["1.0", "2.8", "3.0"] │
|
|
1954
|
+
# # │ b ┆ i64 ┆ ["4", "5", null] │
|
|
1955
|
+
# # │ c ┆ bool ┆ ["true", "false", "true"] │
|
|
1956
|
+
# # │ d ┆ str ┆ [null, ""b"", ""c""] │
|
|
1957
|
+
# # │ e ┆ str ┆ [""usd"", ""eur"", null] │
|
|
1958
|
+
# # │ f ┆ date ┆ ["2020-01-01", "2021-01-02", "… │
|
|
1959
|
+
# # └────────┴───────┴─────────────────────────────────┘
|
|
1960
|
+
def glimpse(
|
|
1961
|
+
max_items_per_column: 10,
|
|
1962
|
+
max_colname_length: 50,
|
|
1963
|
+
return_type: nil
|
|
1964
|
+
)
|
|
1965
|
+
if return_type.nil?
|
|
1966
|
+
return_frame = false
|
|
1967
|
+
else
|
|
1968
|
+
return_frame = return_type == "frame"
|
|
1969
|
+
if !return_frame && !["self", "string"].include?(return_type)
|
|
1970
|
+
msg = "invalid `return_type`; found #{return_type.inspect}, expected one of 'string', 'frame', 'self', or nil"
|
|
1971
|
+
raise ArgumentError, msg
|
|
1265
1972
|
end
|
|
1266
|
-
self.class.new(columns)
|
|
1267
1973
|
end
|
|
1268
1974
|
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1975
|
+
# always print at most this number of values (mainly ensures that
|
|
1976
|
+
# we do not cast long arrays to strings, which would be slow)
|
|
1977
|
+
max_n_values = [max_items_per_column, height].min
|
|
1978
|
+
schema = self.schema
|
|
1979
|
+
|
|
1980
|
+
_column_to_row_output = lambda do |col_name, dtype|
|
|
1981
|
+
fn = schema[col_name] == String ? :inspect : :to_s
|
|
1982
|
+
values = self[0...max_n_values, col_name].to_a
|
|
1983
|
+
if col_name.length > max_colname_length
|
|
1984
|
+
col_name = col_name[0...(max_colname_length - 1)] + "…"
|
|
1985
|
+
end
|
|
1986
|
+
dtype_str = Plr.dtype_str_repr(dtype)
|
|
1987
|
+
if !return_frame
|
|
1988
|
+
dtype_str = "<#{dtype_str}>"
|
|
1989
|
+
end
|
|
1990
|
+
[col_name, dtype_str, values.map { |v| !v.nil? ? v.send(fn) : nil }]
|
|
1991
|
+
end
|
|
1992
|
+
|
|
1993
|
+
data = self.schema.map { |s, dtype| _column_to_row_output.(s, dtype) }
|
|
1994
|
+
|
|
1995
|
+
# output one row per column
|
|
1996
|
+
if return_frame
|
|
1997
|
+
DataFrame.new(
|
|
1998
|
+
data,
|
|
1999
|
+
orient: "row",
|
|
2000
|
+
schema: {"column" => String, "dtype" => String, "values" => List.new(String)}
|
|
1289
2001
|
)
|
|
1290
|
-
|
|
1291
|
-
|
|
2002
|
+
else
|
|
2003
|
+
raise Todo
|
|
2004
|
+
end
|
|
1292
2005
|
end
|
|
1293
2006
|
|
|
1294
|
-
#
|
|
2007
|
+
# Summary statistics for a DataFrame.
|
|
1295
2008
|
#
|
|
1296
|
-
# @param
|
|
1297
|
-
#
|
|
2009
|
+
# @param percentiles [Array]
|
|
2010
|
+
# One or more percentiles to include in the summary statistics.
|
|
2011
|
+
# All values must be in the range `[0, 1]`.
|
|
2012
|
+
# @param interpolation ['nearest', 'higher', 'lower', 'midpoint', 'linear', 'equiprobable']
|
|
2013
|
+
# Interpolation method used when calculating percentiles.
|
|
1298
2014
|
#
|
|
1299
|
-
# @return [
|
|
2015
|
+
# @return [DataFrame]
|
|
2016
|
+
#
|
|
2017
|
+
# @example Show default frame statistics:
|
|
2018
|
+
# df = Polars::DataFrame.new(
|
|
2019
|
+
# {
|
|
2020
|
+
# "float" => [1.0, 2.8, 3.0],
|
|
2021
|
+
# "int" => [40, 50, nil],
|
|
2022
|
+
# "bool" => [true, false, true],
|
|
2023
|
+
# "str" => ["zz", "xx", "yy"],
|
|
2024
|
+
# "date" => [Date.new(2020, 1, 1), Date.new(2021, 7, 5), Date.new(2022, 12, 31)]
|
|
2025
|
+
# }
|
|
2026
|
+
# )
|
|
2027
|
+
# df.describe
|
|
2028
|
+
# # =>
|
|
2029
|
+
# # shape: (9, 6)
|
|
2030
|
+
# # ┌────────────┬──────────┬──────────┬──────────┬──────┬─────────────────────────┐
|
|
2031
|
+
# # │ statistic ┆ float ┆ int ┆ bool ┆ str ┆ date │
|
|
2032
|
+
# # │ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
|
|
2033
|
+
# # │ str ┆ f64 ┆ f64 ┆ f64 ┆ str ┆ str │
|
|
2034
|
+
# # ╞════════════╪══════════╪══════════╪══════════╪══════╪═════════════════════════╡
|
|
2035
|
+
# # │ count ┆ 3.0 ┆ 2.0 ┆ 3.0 ┆ 3 ┆ 3 │
|
|
2036
|
+
# # │ null_count ┆ 0.0 ┆ 1.0 ┆ 0.0 ┆ 0 ┆ 0 │
|
|
2037
|
+
# # │ mean ┆ 2.266667 ┆ 45.0 ┆ 0.666667 ┆ null ┆ 2021-07-02 16:00:00 UTC │
|
|
2038
|
+
# # │ std ┆ 1.101514 ┆ 7.071068 ┆ null ┆ null ┆ null │
|
|
2039
|
+
# # │ min ┆ 1.0 ┆ 40.0 ┆ 0.0 ┆ xx ┆ 2020-01-01 │
|
|
2040
|
+
# # │ 25% ┆ 2.8 ┆ 40.0 ┆ null ┆ null ┆ 2021-07-05 │
|
|
2041
|
+
# # │ 50% ┆ 2.8 ┆ 50.0 ┆ null ┆ null ┆ 2021-07-05 │
|
|
2042
|
+
# # │ 75% ┆ 3.0 ┆ 50.0 ┆ null ┆ null ┆ 2022-12-31 │
|
|
2043
|
+
# # │ max ┆ 3.0 ┆ 50.0 ┆ 1.0 ┆ zz ┆ 2022-12-31 │
|
|
2044
|
+
# # └────────────┴──────────┴──────────┴──────────┴──────┴─────────────────────────┘
|
|
2045
|
+
#
|
|
2046
|
+
# @example Customize which percentiles are displayed, applying linear interpolation:
|
|
2047
|
+
# df.describe(
|
|
2048
|
+
# percentiles: [0.1, 0.3, 0.5, 0.7, 0.9],
|
|
2049
|
+
# interpolation: "linear"
|
|
2050
|
+
# )
|
|
2051
|
+
# # =>
|
|
2052
|
+
# # shape: (11, 6)
|
|
2053
|
+
# # ┌────────────┬──────────┬──────────┬──────────┬──────┬─────────────────────────┐
|
|
2054
|
+
# # │ statistic ┆ float ┆ int ┆ bool ┆ str ┆ date │
|
|
2055
|
+
# # │ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
|
|
2056
|
+
# # │ str ┆ f64 ┆ f64 ┆ f64 ┆ str ┆ str │
|
|
2057
|
+
# # ╞════════════╪══════════╪══════════╪══════════╪══════╪═════════════════════════╡
|
|
2058
|
+
# # │ count ┆ 3.0 ┆ 2.0 ┆ 3.0 ┆ 3 ┆ 3 │
|
|
2059
|
+
# # │ null_count ┆ 0.0 ┆ 1.0 ┆ 0.0 ┆ 0 ┆ 0 │
|
|
2060
|
+
# # │ mean ┆ 2.266667 ┆ 45.0 ┆ 0.666667 ┆ null ┆ 2021-07-02 16:00:00 UTC │
|
|
2061
|
+
# # │ std ┆ 1.101514 ┆ 7.071068 ┆ null ┆ null ┆ null │
|
|
2062
|
+
# # │ min ┆ 1.0 ┆ 40.0 ┆ 0.0 ┆ xx ┆ 2020-01-01 │
|
|
2063
|
+
# # │ … ┆ … ┆ … ┆ … ┆ … ┆ … │
|
|
2064
|
+
# # │ 30% ┆ 2.08 ┆ 43.0 ┆ null ┆ null ┆ 2020-11-26 │
|
|
2065
|
+
# # │ 50% ┆ 2.8 ┆ 45.0 ┆ null ┆ null ┆ 2021-07-05 │
|
|
2066
|
+
# # │ 70% ┆ 2.88 ┆ 47.0 ┆ null ┆ null ┆ 2022-02-07 │
|
|
2067
|
+
# # │ 90% ┆ 2.96 ┆ 49.0 ┆ null ┆ null ┆ 2022-09-13 │
|
|
2068
|
+
# # │ max ┆ 3.0 ┆ 50.0 ┆ 1.0 ┆ zz ┆ 2022-12-31 │
|
|
2069
|
+
# # └────────────┴──────────┴──────────┴──────────┴──────┴─────────────────────────┘
|
|
2070
|
+
def describe(
|
|
2071
|
+
percentiles: [0.25, 0.5, 0.75],
|
|
2072
|
+
interpolation: "nearest"
|
|
2073
|
+
)
|
|
2074
|
+
if columns.empty?
|
|
2075
|
+
msg = "cannot describe a DataFrame that has no columns"
|
|
2076
|
+
raise TypeError, msg
|
|
2077
|
+
end
|
|
2078
|
+
|
|
2079
|
+
lazy.describe(
|
|
2080
|
+
percentiles: percentiles, interpolation: interpolation
|
|
2081
|
+
)
|
|
2082
|
+
end
|
|
2083
|
+
|
|
2084
|
+
# Find the index of a column by name.
|
|
2085
|
+
#
|
|
2086
|
+
# @param name [String]
|
|
2087
|
+
# Name of the column to find.
|
|
2088
|
+
#
|
|
2089
|
+
# @return [Series]
|
|
1300
2090
|
#
|
|
1301
2091
|
# @example
|
|
1302
2092
|
# df = Polars::DataFrame.new(
|
|
@@ -1307,13 +2097,12 @@ module Polars
|
|
|
1307
2097
|
def get_column_index(name)
|
|
1308
2098
|
_df.get_column_index(name)
|
|
1309
2099
|
end
|
|
1310
|
-
alias_method :find_idx_by_name, :get_column_index
|
|
1311
2100
|
|
|
1312
2101
|
# Replace a column at an index location.
|
|
1313
2102
|
#
|
|
1314
2103
|
# @param index [Integer]
|
|
1315
2104
|
# Column index.
|
|
1316
|
-
# @param
|
|
2105
|
+
# @param column [Series]
|
|
1317
2106
|
# Series that will replace the column.
|
|
1318
2107
|
#
|
|
1319
2108
|
# @return [DataFrame]
|
|
@@ -1339,23 +2128,31 @@ module Polars
|
|
|
1339
2128
|
# # │ 20 ┆ 7 ┆ b │
|
|
1340
2129
|
# # │ 30 ┆ 8 ┆ c │
|
|
1341
2130
|
# # └───────┴─────┴─────┘
|
|
1342
|
-
def replace_column(index,
|
|
2131
|
+
def replace_column(index, column)
|
|
1343
2132
|
if index < 0
|
|
1344
|
-
index =
|
|
2133
|
+
index = width + index
|
|
1345
2134
|
end
|
|
1346
|
-
_df.replace_column(index,
|
|
2135
|
+
_df.replace_column(index, column._s)
|
|
1347
2136
|
self
|
|
1348
2137
|
end
|
|
1349
|
-
alias_method :replace_at_idx, :replace_column
|
|
1350
2138
|
|
|
1351
|
-
# Sort the
|
|
2139
|
+
# Sort the dataframe by the given columns.
|
|
1352
2140
|
#
|
|
1353
|
-
# @param by [
|
|
1354
|
-
#
|
|
1355
|
-
#
|
|
1356
|
-
#
|
|
2141
|
+
# @param by [Object]
|
|
2142
|
+
# Column(s) to sort by. Accepts expression input, including selectors. Strings
|
|
2143
|
+
# are parsed as column names.
|
|
2144
|
+
# @param more_by [Array]
|
|
2145
|
+
# Additional columns to sort by, specified as positional arguments.
|
|
2146
|
+
# @param descending [Boolean]
|
|
2147
|
+
# Sort in descending order. When sorting by multiple columns, can be specified
|
|
2148
|
+
# per column by passing an array of booleans.
|
|
1357
2149
|
# @param nulls_last [Boolean]
|
|
1358
|
-
# Place null values last
|
|
2150
|
+
# Place null values last; can specify a single boolean applying to all columns
|
|
2151
|
+
# or an array of booleans for per-column control.
|
|
2152
|
+
# @param multithreaded [Boolean]
|
|
2153
|
+
# Sort using multiple threads.
|
|
2154
|
+
# @param maintain_order [Boolean]
|
|
2155
|
+
# Whether the order should be maintained if elements are equal.
|
|
1359
2156
|
#
|
|
1360
2157
|
# @return [DataFrame]
|
|
1361
2158
|
#
|
|
@@ -1367,7 +2164,7 @@ module Polars
|
|
|
1367
2164
|
# "ham" => ["a", "b", "c"]
|
|
1368
2165
|
# }
|
|
1369
2166
|
# )
|
|
1370
|
-
# df.sort("foo",
|
|
2167
|
+
# df.sort("foo", descending: true)
|
|
1371
2168
|
# # =>
|
|
1372
2169
|
# # shape: (3, 3)
|
|
1373
2170
|
# # ┌─────┬─────┬─────┐
|
|
@@ -1383,7 +2180,7 @@ module Polars
|
|
|
1383
2180
|
# @example Sort by multiple columns.
|
|
1384
2181
|
# df.sort(
|
|
1385
2182
|
# [Polars.col("foo"), Polars.col("bar")**2],
|
|
1386
|
-
#
|
|
2183
|
+
# descending: [true, false]
|
|
1387
2184
|
# )
|
|
1388
2185
|
# # =>
|
|
1389
2186
|
# # shape: (3, 3)
|
|
@@ -1396,24 +2193,255 @@ module Polars
|
|
|
1396
2193
|
# # │ 2 ┆ 7.0 ┆ b │
|
|
1397
2194
|
# # │ 1 ┆ 6.0 ┆ a │
|
|
1398
2195
|
# # └─────┴─────┴─────┘
|
|
1399
|
-
def sort(
|
|
2196
|
+
def sort(
|
|
2197
|
+
by,
|
|
2198
|
+
*more_by,
|
|
2199
|
+
descending: false,
|
|
2200
|
+
nulls_last: false,
|
|
2201
|
+
multithreaded: true,
|
|
2202
|
+
maintain_order: false
|
|
2203
|
+
)
|
|
1400
2204
|
lazy
|
|
1401
|
-
.sort(
|
|
1402
|
-
|
|
2205
|
+
.sort(
|
|
2206
|
+
by,
|
|
2207
|
+
*more_by,
|
|
2208
|
+
descending: descending,
|
|
2209
|
+
nulls_last: nulls_last,
|
|
2210
|
+
multithreaded: multithreaded,
|
|
2211
|
+
maintain_order: maintain_order
|
|
2212
|
+
)
|
|
2213
|
+
.collect(optimizations: QueryOptFlags._eager)
|
|
1403
2214
|
end
|
|
1404
2215
|
|
|
1405
2216
|
# Sort the DataFrame by column in-place.
|
|
1406
2217
|
#
|
|
1407
2218
|
# @param by [String]
|
|
1408
2219
|
# By which column to sort.
|
|
1409
|
-
# @param
|
|
2220
|
+
# @param descending [Boolean]
|
|
1410
2221
|
# Reverse/descending sort.
|
|
1411
2222
|
# @param nulls_last [Boolean]
|
|
1412
2223
|
# Place null values last. Can only be used if sorted by a single column.
|
|
1413
2224
|
#
|
|
1414
2225
|
# @return [DataFrame]
|
|
1415
|
-
def sort!(by,
|
|
1416
|
-
self._df = sort(by,
|
|
2226
|
+
def sort!(by, descending: false, nulls_last: false)
|
|
2227
|
+
self._df = sort(by, descending: descending, nulls_last: nulls_last)._df
|
|
2228
|
+
end
|
|
2229
|
+
|
|
2230
|
+
# Execute a SQL query against the DataFrame.
|
|
2231
|
+
#
|
|
2232
|
+
# @note
|
|
2233
|
+
# This functionality is considered **unstable**, although it is close to
|
|
2234
|
+
# being considered stable. It may be changed at any point without it being
|
|
2235
|
+
# considered a breaking change.
|
|
2236
|
+
#
|
|
2237
|
+
# @param query [String]
|
|
2238
|
+
# SQL query to execute.
|
|
2239
|
+
# @param table_name [String]
|
|
2240
|
+
# Optionally provide an explicit name for the table that represents the
|
|
2241
|
+
# calling frame (defaults to "self").
|
|
2242
|
+
#
|
|
2243
|
+
# @return [DataFrame]
|
|
2244
|
+
#
|
|
2245
|
+
# @note
|
|
2246
|
+
# * The calling frame is automatically registered as a table in the SQL context
|
|
2247
|
+
# under the name "self". If you want access to the DataFrames and LazyFrames
|
|
2248
|
+
# found in the current globals, use the top-level :meth:`pl.sql <polars.sql>`.
|
|
2249
|
+
# * More control over registration and execution behaviour is available by
|
|
2250
|
+
# using the :class:`SQLContext` object.
|
|
2251
|
+
# * The SQL query executes in lazy mode before being collected and returned
|
|
2252
|
+
# as a DataFrame.
|
|
2253
|
+
#
|
|
2254
|
+
# @example Query the DataFrame using SQL:
|
|
2255
|
+
# df1 = Polars::DataFrame.new(
|
|
2256
|
+
# {
|
|
2257
|
+
# "a" => [1, 2, 3],
|
|
2258
|
+
# "b" => ["zz", "yy", "xx"],
|
|
2259
|
+
# "c" => [Date.new(1999, 12, 31), Date.new(2010, 10, 10), Date.new(2077, 8, 8)]
|
|
2260
|
+
# }
|
|
2261
|
+
# )
|
|
2262
|
+
# df1.sql("SELECT c, b FROM self WHERE a > 1")
|
|
2263
|
+
# # =>
|
|
2264
|
+
# # shape: (2, 2)
|
|
2265
|
+
# # ┌────────────┬─────┐
|
|
2266
|
+
# # │ c ┆ b │
|
|
2267
|
+
# # │ --- ┆ --- │
|
|
2268
|
+
# # │ date ┆ str │
|
|
2269
|
+
# # ╞════════════╪═════╡
|
|
2270
|
+
# # │ 2010-10-10 ┆ yy │
|
|
2271
|
+
# # │ 2077-08-08 ┆ xx │
|
|
2272
|
+
# # └────────────┴─────┘
|
|
2273
|
+
#
|
|
2274
|
+
# @example Apply transformations to a DataFrame using SQL, aliasing "self" to "frame".
|
|
2275
|
+
# df1.sql(
|
|
2276
|
+
# "
|
|
2277
|
+
# SELECT
|
|
2278
|
+
# a,
|
|
2279
|
+
# (a % 2 == 0) AS a_is_even,
|
|
2280
|
+
# CONCAT_WS(':', b, b) AS b_b,
|
|
2281
|
+
# EXTRACT(year FROM c) AS year,
|
|
2282
|
+
# 0::float4 AS \"zero\",
|
|
2283
|
+
# FROM frame
|
|
2284
|
+
# ",
|
|
2285
|
+
# table_name: "frame"
|
|
2286
|
+
# )
|
|
2287
|
+
# # =>
|
|
2288
|
+
# # shape: (3, 5)
|
|
2289
|
+
# # ┌─────┬───────────┬───────┬──────┬──────┐
|
|
2290
|
+
# # │ a ┆ a_is_even ┆ b_b ┆ year ┆ zero │
|
|
2291
|
+
# # │ --- ┆ --- ┆ --- ┆ --- ┆ --- │
|
|
2292
|
+
# # │ i64 ┆ bool ┆ str ┆ i32 ┆ f32 │
|
|
2293
|
+
# # ╞═════╪═══════════╪═══════╪══════╪══════╡
|
|
2294
|
+
# # │ 1 ┆ false ┆ zz:zz ┆ 1999 ┆ 0.0 │
|
|
2295
|
+
# # │ 2 ┆ true ┆ yy:yy ┆ 2010 ┆ 0.0 │
|
|
2296
|
+
# # │ 3 ┆ false ┆ xx:xx ┆ 2077 ┆ 0.0 │
|
|
2297
|
+
# # └─────┴───────────┴───────┴──────┴──────┘
|
|
2298
|
+
def sql(query, table_name: "self")
|
|
2299
|
+
ctx = SQLContext.new(eager: true)
|
|
2300
|
+
name = table_name || "self"
|
|
2301
|
+
ctx.register(name, self)
|
|
2302
|
+
ctx.execute(query)
|
|
2303
|
+
end
|
|
2304
|
+
|
|
2305
|
+
# Return the `k` largest rows.
|
|
2306
|
+
#
|
|
2307
|
+
# Non-null elements are always preferred over null elements, regardless of
|
|
2308
|
+
# the value of `reverse`. The output is not guaranteed to be in any
|
|
2309
|
+
# particular order, call `sort` after this function if you wish the
|
|
2310
|
+
# output to be sorted.
|
|
2311
|
+
#
|
|
2312
|
+
# @param k [Integer]
|
|
2313
|
+
# Number of rows to return.
|
|
2314
|
+
# @param by [Object]
|
|
2315
|
+
# Column(s) used to determine the top rows.
|
|
2316
|
+
# Accepts expression input. Strings are parsed as column names.
|
|
2317
|
+
# @param reverse [Object]
|
|
2318
|
+
# Consider the `k` smallest elements of the `by` column(s) (instead of the `k`
|
|
2319
|
+
# largest). This can be specified per column by passing an array of
|
|
2320
|
+
# booleans.
|
|
2321
|
+
#
|
|
2322
|
+
# @return [DataFrame]
|
|
2323
|
+
#
|
|
2324
|
+
# @example Get the rows which contain the 4 largest values in column b.
|
|
2325
|
+
# df = Polars::DataFrame.new(
|
|
2326
|
+
# {
|
|
2327
|
+
# "a" => ["a", "b", "a", "b", "b", "c"],
|
|
2328
|
+
# "b" => [2, 1, 1, 3, 2, 1]
|
|
2329
|
+
# }
|
|
2330
|
+
# )
|
|
2331
|
+
# df.top_k(4, by: "b")
|
|
2332
|
+
# # =>
|
|
2333
|
+
# # shape: (4, 2)
|
|
2334
|
+
# # ┌─────┬─────┐
|
|
2335
|
+
# # │ a ┆ b │
|
|
2336
|
+
# # │ --- ┆ --- │
|
|
2337
|
+
# # │ str ┆ i64 │
|
|
2338
|
+
# # ╞═════╪═════╡
|
|
2339
|
+
# # │ b ┆ 3 │
|
|
2340
|
+
# # │ a ┆ 2 │
|
|
2341
|
+
# # │ b ┆ 2 │
|
|
2342
|
+
# # │ b ┆ 1 │
|
|
2343
|
+
# # └─────┴─────┘
|
|
2344
|
+
#
|
|
2345
|
+
# @example Get the rows which contain the 4 largest values when sorting on column b and a.
|
|
2346
|
+
# df.top_k(4, by: ["b", "a"])
|
|
2347
|
+
# # =>
|
|
2348
|
+
# # shape: (4, 2)
|
|
2349
|
+
# # ┌─────┬─────┐
|
|
2350
|
+
# # │ a ┆ b │
|
|
2351
|
+
# # │ --- ┆ --- │
|
|
2352
|
+
# # │ str ┆ i64 │
|
|
2353
|
+
# # ╞═════╪═════╡
|
|
2354
|
+
# # │ b ┆ 3 │
|
|
2355
|
+
# # │ b ┆ 2 │
|
|
2356
|
+
# # │ a ┆ 2 │
|
|
2357
|
+
# # │ c ┆ 1 │
|
|
2358
|
+
# # └─────┴─────┘
|
|
2359
|
+
def top_k(
|
|
2360
|
+
k,
|
|
2361
|
+
by:,
|
|
2362
|
+
reverse: false
|
|
2363
|
+
)
|
|
2364
|
+
lazy
|
|
2365
|
+
.top_k(k, by: by, reverse: reverse)
|
|
2366
|
+
.collect(
|
|
2367
|
+
optimizations: QueryOptFlags.new(
|
|
2368
|
+
projection_pushdown: false,
|
|
2369
|
+
predicate_pushdown: false,
|
|
2370
|
+
comm_subplan_elim: false,
|
|
2371
|
+
slice_pushdown: true
|
|
2372
|
+
)
|
|
2373
|
+
)
|
|
2374
|
+
end
|
|
2375
|
+
|
|
2376
|
+
# Return the `k` smallest rows.
|
|
2377
|
+
#
|
|
2378
|
+
# Non-null elements are always preferred over null elements, regardless of
|
|
2379
|
+
# the value of `reverse`. The output is not guaranteed to be in any
|
|
2380
|
+
# particular order, call `sort` after this function if you wish the
|
|
2381
|
+
# output to be sorted.
|
|
2382
|
+
#
|
|
2383
|
+
# @param k [Integer]
|
|
2384
|
+
# Number of rows to return.
|
|
2385
|
+
# @param by [Object]
|
|
2386
|
+
# Column(s) used to determine the bottom rows.
|
|
2387
|
+
# Accepts expression input. Strings are parsed as column names.
|
|
2388
|
+
# @param reverse [Object]
|
|
2389
|
+
# Consider the `k` largest elements of the `by` column(s) (instead of the `k`
|
|
2390
|
+
# smallest). This can be specified per column by passing an array of
|
|
2391
|
+
# booleans.
|
|
2392
|
+
#
|
|
2393
|
+
# @return [DataFrame]
|
|
2394
|
+
#
|
|
2395
|
+
# @example Get the rows which contain the 4 smallest values in column b.
|
|
2396
|
+
# df = Polars::DataFrame.new(
|
|
2397
|
+
# {
|
|
2398
|
+
# "a" => ["a", "b", "a", "b", "b", "c"],
|
|
2399
|
+
# "b" => [2, 1, 1, 3, 2, 1]
|
|
2400
|
+
# }
|
|
2401
|
+
# )
|
|
2402
|
+
# df.bottom_k(4, by: "b")
|
|
2403
|
+
# # =>
|
|
2404
|
+
# # shape: (4, 2)
|
|
2405
|
+
# # ┌─────┬─────┐
|
|
2406
|
+
# # │ a ┆ b │
|
|
2407
|
+
# # │ --- ┆ --- │
|
|
2408
|
+
# # │ str ┆ i64 │
|
|
2409
|
+
# # ╞═════╪═════╡
|
|
2410
|
+
# # │ b ┆ 1 │
|
|
2411
|
+
# # │ a ┆ 1 │
|
|
2412
|
+
# # │ c ┆ 1 │
|
|
2413
|
+
# # │ a ┆ 2 │
|
|
2414
|
+
# # └─────┴─────┘
|
|
2415
|
+
#
|
|
2416
|
+
# @example Get the rows which contain the 4 smallest values when sorting on column a and b.
|
|
2417
|
+
# df.bottom_k(4, by: ["a", "b"])
|
|
2418
|
+
# # =>
|
|
2419
|
+
# # shape: (4, 2)
|
|
2420
|
+
# # ┌─────┬─────┐
|
|
2421
|
+
# # │ a ┆ b │
|
|
2422
|
+
# # │ --- ┆ --- │
|
|
2423
|
+
# # │ str ┆ i64 │
|
|
2424
|
+
# # ╞═════╪═════╡
|
|
2425
|
+
# # │ a ┆ 1 │
|
|
2426
|
+
# # │ a ┆ 2 │
|
|
2427
|
+
# # │ b ┆ 1 │
|
|
2428
|
+
# # │ b ┆ 2 │
|
|
2429
|
+
# # └─────┴─────┘
|
|
2430
|
+
def bottom_k(
|
|
2431
|
+
k,
|
|
2432
|
+
by:,
|
|
2433
|
+
reverse: false
|
|
2434
|
+
)
|
|
2435
|
+
lazy
|
|
2436
|
+
.bottom_k(k, by: by, reverse: reverse)
|
|
2437
|
+
.collect(
|
|
2438
|
+
optimizations: QueryOptFlags.new(
|
|
2439
|
+
projection_pushdown: false,
|
|
2440
|
+
predicate_pushdown: false,
|
|
2441
|
+
comm_subplan_elim: false,
|
|
2442
|
+
slice_pushdown: true
|
|
2443
|
+
)
|
|
2444
|
+
)
|
|
1417
2445
|
end
|
|
1418
2446
|
|
|
1419
2447
|
# Check if DataFrame is equal to other.
|
|
@@ -1447,36 +2475,6 @@ module Polars
|
|
|
1447
2475
|
def equals(other, null_equal: true)
|
|
1448
2476
|
_df.equals(other._df, null_equal)
|
|
1449
2477
|
end
|
|
1450
|
-
alias_method :frame_equal, :equals
|
|
1451
|
-
|
|
1452
|
-
# Replace a column by a new Series.
|
|
1453
|
-
#
|
|
1454
|
-
# @param column [String]
|
|
1455
|
-
# Column to replace.
|
|
1456
|
-
# @param new_col [Series]
|
|
1457
|
-
# New column to insert.
|
|
1458
|
-
#
|
|
1459
|
-
# @return [DataFrame]
|
|
1460
|
-
#
|
|
1461
|
-
# @example
|
|
1462
|
-
# df = Polars::DataFrame.new({"foo" => [1, 2, 3], "bar" => [4, 5, 6]})
|
|
1463
|
-
# s = Polars::Series.new([10, 20, 30])
|
|
1464
|
-
# df.replace("foo", s)
|
|
1465
|
-
# # =>
|
|
1466
|
-
# # shape: (3, 2)
|
|
1467
|
-
# # ┌─────┬─────┐
|
|
1468
|
-
# # │ foo ┆ bar │
|
|
1469
|
-
# # │ --- ┆ --- │
|
|
1470
|
-
# # │ i64 ┆ i64 │
|
|
1471
|
-
# # ╞═════╪═════╡
|
|
1472
|
-
# # │ 10 ┆ 4 │
|
|
1473
|
-
# # │ 20 ┆ 5 │
|
|
1474
|
-
# # │ 30 ┆ 6 │
|
|
1475
|
-
# # └─────┴─────┘
|
|
1476
|
-
def replace(column, new_col)
|
|
1477
|
-
_df.replace(column.to_s, new_col._s)
|
|
1478
|
-
self
|
|
1479
|
-
end
|
|
1480
2478
|
|
|
1481
2479
|
# Get a slice of this DataFrame.
|
|
1482
2480
|
#
|
|
@@ -1606,10 +2604,59 @@ module Polars
|
|
|
1606
2604
|
_from_rbdf(_df.tail(n))
|
|
1607
2605
|
end
|
|
1608
2606
|
|
|
1609
|
-
#
|
|
2607
|
+
# Drop all rows that contain one or more NaN values.
|
|
2608
|
+
#
|
|
2609
|
+
# The original order of the remaining rows is preserved.
|
|
2610
|
+
#
|
|
2611
|
+
# @param subset [Object]
|
|
2612
|
+
# Column name(s) for which NaN values are considered; if set to `nil`
|
|
2613
|
+
# (default), use all columns (note that only floating-point columns
|
|
2614
|
+
# can contain NaNs).
|
|
2615
|
+
#
|
|
2616
|
+
# @return [DataFrame]
|
|
2617
|
+
#
|
|
2618
|
+
# @example
|
|
2619
|
+
# df = Polars::DataFrame.new(
|
|
2620
|
+
# {
|
|
2621
|
+
# "foo" => [-20.5, Float::NAN, 80.0],
|
|
2622
|
+
# "bar" => [Float::NAN, 110.0, 25.5],
|
|
2623
|
+
# "ham" => ["xxx", "yyy", nil]
|
|
2624
|
+
# }
|
|
2625
|
+
# )
|
|
2626
|
+
# df.drop_nans
|
|
2627
|
+
# # =>
|
|
2628
|
+
# # shape: (1, 3)
|
|
2629
|
+
# # ┌──────┬──────┬──────┐
|
|
2630
|
+
# # │ foo ┆ bar ┆ ham │
|
|
2631
|
+
# # │ --- ┆ --- ┆ --- │
|
|
2632
|
+
# # │ f64 ┆ f64 ┆ str │
|
|
2633
|
+
# # ╞══════╪══════╪══════╡
|
|
2634
|
+
# # │ 80.0 ┆ 25.5 ┆ null │
|
|
2635
|
+
# # └──────┴──────┴──────┘
|
|
2636
|
+
#
|
|
2637
|
+
# @example
|
|
2638
|
+
# df.drop_nans(subset: ["bar"])
|
|
2639
|
+
# # =>
|
|
2640
|
+
# # shape: (2, 3)
|
|
2641
|
+
# # ┌──────┬───────┬──────┐
|
|
2642
|
+
# # │ foo ┆ bar ┆ ham │
|
|
2643
|
+
# # │ --- ┆ --- ┆ --- │
|
|
2644
|
+
# # │ f64 ┆ f64 ┆ str │
|
|
2645
|
+
# # ╞══════╪═══════╪══════╡
|
|
2646
|
+
# # │ NaN ┆ 110.0 ┆ yyy │
|
|
2647
|
+
# # │ 80.0 ┆ 25.5 ┆ null │
|
|
2648
|
+
# # └──────┴───────┴──────┘
|
|
2649
|
+
def drop_nans(subset: nil)
|
|
2650
|
+
lazy.drop_nans(subset: subset).collect(optimizations: QueryOptFlags._eager)
|
|
2651
|
+
end
|
|
2652
|
+
|
|
2653
|
+
# Drop all rows that contain one or more null values.
|
|
2654
|
+
#
|
|
2655
|
+
# The original order of the remaining rows is preserved.
|
|
1610
2656
|
#
|
|
1611
2657
|
# @param subset [Object]
|
|
1612
|
-
#
|
|
2658
|
+
# Column name(s) for which null values are considered.
|
|
2659
|
+
# If set to `nil` (default), use all columns.
|
|
1613
2660
|
#
|
|
1614
2661
|
# @return [DataFrame]
|
|
1615
2662
|
#
|
|
@@ -1618,27 +2665,39 @@ module Polars
|
|
|
1618
2665
|
# {
|
|
1619
2666
|
# "foo" => [1, 2, 3],
|
|
1620
2667
|
# "bar" => [6, nil, 8],
|
|
1621
|
-
# "ham" => ["a", "b",
|
|
2668
|
+
# "ham" => ["a", "b", nil]
|
|
1622
2669
|
# }
|
|
1623
2670
|
# )
|
|
1624
2671
|
# df.drop_nulls
|
|
1625
2672
|
# # =>
|
|
1626
|
-
# # shape: (
|
|
2673
|
+
# # shape: (1, 3)
|
|
1627
2674
|
# # ┌─────┬─────┬─────┐
|
|
1628
2675
|
# # │ foo ┆ bar ┆ ham │
|
|
1629
2676
|
# # │ --- ┆ --- ┆ --- │
|
|
1630
2677
|
# # │ i64 ┆ i64 ┆ str │
|
|
1631
2678
|
# # ╞═════╪═════╪═════╡
|
|
1632
2679
|
# # │ 1 ┆ 6 ┆ a │
|
|
1633
|
-
# # │ 3 ┆ 8 ┆ c │
|
|
1634
2680
|
# # └─────┴─────┴─────┘
|
|
2681
|
+
#
|
|
2682
|
+
# @example
|
|
2683
|
+
# df.drop_nulls(subset: Polars.cs.integer)
|
|
2684
|
+
# # =>
|
|
2685
|
+
# # shape: (2, 3)
|
|
2686
|
+
# # ┌─────┬─────┬──────┐
|
|
2687
|
+
# # │ foo ┆ bar ┆ ham │
|
|
2688
|
+
# # │ --- ┆ --- ┆ --- │
|
|
2689
|
+
# # │ i64 ┆ i64 ┆ str │
|
|
2690
|
+
# # ╞═════╪═════╪══════╡
|
|
2691
|
+
# # │ 1 ┆ 6 ┆ a │
|
|
2692
|
+
# # │ 3 ┆ 8 ┆ null │
|
|
2693
|
+
# # └─────┴─────┴──────┘
|
|
1635
2694
|
def drop_nulls(subset: nil)
|
|
1636
|
-
lazy.drop_nulls(subset: subset).collect(
|
|
2695
|
+
lazy.drop_nulls(subset: subset).collect(optimizations: QueryOptFlags._eager)
|
|
1637
2696
|
end
|
|
1638
2697
|
|
|
1639
2698
|
# Offers a structured way to apply a sequence of user-defined functions (UDFs).
|
|
1640
2699
|
#
|
|
1641
|
-
# @param
|
|
2700
|
+
# @param function [Object]
|
|
1642
2701
|
# Callable; will receive the frame as the first parameter,
|
|
1643
2702
|
# followed by any given args/kwargs.
|
|
1644
2703
|
# @param args [Object]
|
|
@@ -1655,7 +2714,7 @@ module Polars
|
|
|
1655
2714
|
#
|
|
1656
2715
|
# @example
|
|
1657
2716
|
# cast_str_to_int = lambda do |data, col_name:|
|
|
1658
|
-
# data.
|
|
2717
|
+
# data.with_columns(Polars.col(col_name).cast(Polars::Int64))
|
|
1659
2718
|
# end
|
|
1660
2719
|
#
|
|
1661
2720
|
# df = Polars::DataFrame.new({"a" => [1, 2, 3, 4], "b" => ["10", "20", "30", "40"]})
|
|
@@ -1672,8 +2731,94 @@ module Polars
|
|
|
1672
2731
|
# # │ 3 ┆ 30 │
|
|
1673
2732
|
# # │ 4 ┆ 40 │
|
|
1674
2733
|
# # └─────┴─────┘
|
|
1675
|
-
def pipe(
|
|
1676
|
-
|
|
2734
|
+
def pipe(function, *args, **kwargs, &block)
|
|
2735
|
+
function.(self, *args, **kwargs, &block)
|
|
2736
|
+
end
|
|
2737
|
+
|
|
2738
|
+
# Apply eager functions to columns of a DataFrame.
|
|
2739
|
+
#
|
|
2740
|
+
# Users should always prefer :meth:`with_columns` unless they are using
|
|
2741
|
+
# expressions that are only possible on `Series` and not on `Expr`. This is almost
|
|
2742
|
+
# never the case, except for a very select few functions that cannot know the
|
|
2743
|
+
# output datatype without looking at the data.
|
|
2744
|
+
#
|
|
2745
|
+
# @param column_names [Object]
|
|
2746
|
+
# The columns to apply the UDF to.
|
|
2747
|
+
# @param args [Array]
|
|
2748
|
+
# Arguments to pass to the UDF.
|
|
2749
|
+
# @param kwargs [Hash]
|
|
2750
|
+
# Keyword arguments to pass to the UDF.
|
|
2751
|
+
#
|
|
2752
|
+
# @return [DataFrame]
|
|
2753
|
+
#
|
|
2754
|
+
# @example
|
|
2755
|
+
# df = Polars::DataFrame.new({"a" => [1, 2, 3, 4], "b" => ["10", "20", "30", "40"]})
|
|
2756
|
+
# df.map_columns("a") { |s| s.shrink_dtype }
|
|
2757
|
+
# # =>
|
|
2758
|
+
# # shape: (4, 2)
|
|
2759
|
+
# # ┌─────┬─────┐
|
|
2760
|
+
# # │ a ┆ b │
|
|
2761
|
+
# # │ --- ┆ --- │
|
|
2762
|
+
# # │ i8 ┆ str │
|
|
2763
|
+
# # ╞═════╪═════╡
|
|
2764
|
+
# # │ 1 ┆ 10 │
|
|
2765
|
+
# # │ 2 ┆ 20 │
|
|
2766
|
+
# # │ 3 ┆ 30 │
|
|
2767
|
+
# # │ 4 ┆ 40 │
|
|
2768
|
+
# # └─────┴─────┘
|
|
2769
|
+
#
|
|
2770
|
+
# @example
|
|
2771
|
+
# df = Polars::DataFrame.new(
|
|
2772
|
+
# {
|
|
2773
|
+
# "a" => ['{"x":"a"}', nil, '{"x":"b"}', nil],
|
|
2774
|
+
# "b" => ['{"a":1, "b": true}', nil, '{"a":2, "b": false}', nil]
|
|
2775
|
+
# }
|
|
2776
|
+
# )
|
|
2777
|
+
# df.map_columns(["a", "b"]) { |s| s.str.json_decode }
|
|
2778
|
+
# # =>
|
|
2779
|
+
# # shape: (4, 2)
|
|
2780
|
+
# # ┌───────────┬───────────┐
|
|
2781
|
+
# # │ a ┆ b │
|
|
2782
|
+
# # │ --- ┆ --- │
|
|
2783
|
+
# # │ struct[1] ┆ struct[2] │
|
|
2784
|
+
# # ╞═══════════╪═══════════╡
|
|
2785
|
+
# # │ {"a"} ┆ {1,true} │
|
|
2786
|
+
# # │ null ┆ null │
|
|
2787
|
+
# # │ {"b"} ┆ {2,false} │
|
|
2788
|
+
# # │ null ┆ null │
|
|
2789
|
+
# # └───────────┴───────────┘
|
|
2790
|
+
#
|
|
2791
|
+
# @example
|
|
2792
|
+
# df.map_columns(Polars.cs.all) { |s| s.str.json_decode }
|
|
2793
|
+
# # =>
|
|
2794
|
+
# # shape: (4, 2)
|
|
2795
|
+
# # ┌───────────┬───────────┐
|
|
2796
|
+
# # │ a ┆ b │
|
|
2797
|
+
# # │ --- ┆ --- │
|
|
2798
|
+
# # │ struct[1] ┆ struct[2] │
|
|
2799
|
+
# # ╞═══════════╪═══════════╡
|
|
2800
|
+
# # │ {"a"} ┆ {1,true} │
|
|
2801
|
+
# # │ null ┆ null │
|
|
2802
|
+
# # │ {"b"} ┆ {2,false} │
|
|
2803
|
+
# # │ null ┆ null │
|
|
2804
|
+
# # └───────────┴───────────┘
|
|
2805
|
+
def map_columns(
|
|
2806
|
+
column_names,
|
|
2807
|
+
*args,
|
|
2808
|
+
**kwargs,
|
|
2809
|
+
&function
|
|
2810
|
+
)
|
|
2811
|
+
if column_names.is_a?(Selector) || column_names.is_a?(Expr)
|
|
2812
|
+
c_names = Array(Utils.expand_selector(self, column_names))
|
|
2813
|
+
elsif Utils.strlike?(column_names)
|
|
2814
|
+
c_names = [column_names]
|
|
2815
|
+
else
|
|
2816
|
+
c_names = Array(column_names)
|
|
2817
|
+
end
|
|
2818
|
+
|
|
2819
|
+
with_columns(
|
|
2820
|
+
c_names.map { |c| function.call(self[c], *args, **kwargs) }
|
|
2821
|
+
)
|
|
1677
2822
|
end
|
|
1678
2823
|
|
|
1679
2824
|
# Add a column at index 0 that counts the rows.
|
|
@@ -1707,28 +2852,30 @@ module Polars
|
|
|
1707
2852
|
def with_row_index(name: "index", offset: 0)
|
|
1708
2853
|
_from_rbdf(_df.with_row_index(name, offset))
|
|
1709
2854
|
end
|
|
1710
|
-
alias_method :with_row_count, :with_row_index
|
|
1711
2855
|
|
|
1712
2856
|
# Start a group by operation.
|
|
1713
2857
|
#
|
|
1714
|
-
# @param by [
|
|
2858
|
+
# @param by [Array]
|
|
1715
2859
|
# Column(s) to group by.
|
|
1716
2860
|
# @param maintain_order [Boolean]
|
|
1717
2861
|
# Make sure that the order of the groups remain consistent. This is more
|
|
1718
2862
|
# expensive than a default group by. Note that this only works in expression
|
|
1719
2863
|
# aggregations.
|
|
2864
|
+
# @param named_by [Hash]
|
|
2865
|
+
# Additional columns to group by, specified as keyword arguments.
|
|
2866
|
+
# The columns will be renamed to the keyword used.
|
|
1720
2867
|
#
|
|
1721
2868
|
# @return [GroupBy]
|
|
1722
2869
|
#
|
|
1723
|
-
# @example
|
|
2870
|
+
# @example Group by one column and call `agg` to compute the grouped sum of another column.
|
|
1724
2871
|
# df = Polars::DataFrame.new(
|
|
1725
2872
|
# {
|
|
1726
|
-
# "a" => ["a", "b", "a", "b", "
|
|
1727
|
-
# "b" => [1, 2,
|
|
1728
|
-
# "c" => [
|
|
2873
|
+
# "a" => ["a", "b", "a", "b", "c"],
|
|
2874
|
+
# "b" => [1, 2, 1, 3, 3],
|
|
2875
|
+
# "c" => [5, 4, 3, 2, 1]
|
|
1729
2876
|
# }
|
|
1730
2877
|
# )
|
|
1731
|
-
# df.group_by("a").agg(Polars.col("b").sum)
|
|
2878
|
+
# df.group_by("a").agg(Polars.col("b").sum)
|
|
1732
2879
|
# # =>
|
|
1733
2880
|
# # shape: (3, 2)
|
|
1734
2881
|
# # ┌─────┬─────┐
|
|
@@ -1736,27 +2883,76 @@ module Polars
|
|
|
1736
2883
|
# # │ --- ┆ --- │
|
|
1737
2884
|
# # │ str ┆ i64 │
|
|
1738
2885
|
# # ╞═════╪═════╡
|
|
1739
|
-
# # │ a ┆
|
|
1740
|
-
# # │ b ┆
|
|
1741
|
-
# # │ c ┆
|
|
2886
|
+
# # │ a ┆ 2 │
|
|
2887
|
+
# # │ b ┆ 5 │
|
|
2888
|
+
# # │ c ┆ 3 │
|
|
1742
2889
|
# # └─────┴─────┘
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
2890
|
+
#
|
|
2891
|
+
# @example Set `maintain_order: true` to ensure the order of the groups is consistent with the input.
|
|
2892
|
+
# df.group_by("a", maintain_order: true).agg(Polars.col("c"))
|
|
2893
|
+
# # =>
|
|
2894
|
+
# # shape: (3, 2)
|
|
2895
|
+
# # ┌─────┬───────────┐
|
|
2896
|
+
# # │ a ┆ c │
|
|
2897
|
+
# # │ --- ┆ --- │
|
|
2898
|
+
# # │ str ┆ list[i64] │
|
|
2899
|
+
# # ╞═════╪═══════════╡
|
|
2900
|
+
# # │ a ┆ [5, 3] │
|
|
2901
|
+
# # │ b ┆ [4, 2] │
|
|
2902
|
+
# # │ c ┆ [1] │
|
|
2903
|
+
# # └─────┴───────────┘
|
|
2904
|
+
#
|
|
2905
|
+
# @example Group by multiple columns by passing a list of column names.
|
|
2906
|
+
# df.group_by(["a", "b"]).agg(Polars.max("c"))
|
|
2907
|
+
# # =>
|
|
2908
|
+
# # shape: (4, 3)
|
|
2909
|
+
# # ┌─────┬─────┬─────┐
|
|
2910
|
+
# # │ a ┆ b ┆ c │
|
|
2911
|
+
# # │ --- ┆ --- ┆ --- │
|
|
2912
|
+
# # │ str ┆ i64 ┆ i64 │
|
|
2913
|
+
# # ╞═════╪═════╪═════╡
|
|
2914
|
+
# # │ a ┆ 1 ┆ 5 │
|
|
2915
|
+
# # │ b ┆ 2 ┆ 4 │
|
|
2916
|
+
# # │ b ┆ 3 ┆ 2 │
|
|
2917
|
+
# # │ c ┆ 3 ┆ 1 │
|
|
2918
|
+
# # └─────┴─────┴─────┘
|
|
2919
|
+
#
|
|
2920
|
+
# @example Or use positional arguments to group by multiple columns in the same way. Expressions are also accepted.
|
|
2921
|
+
# df.group_by("a", Polars.col("b") / 2).agg(Polars.col("c").mean)
|
|
2922
|
+
# # =>
|
|
2923
|
+
# # shape: (3, 3)
|
|
2924
|
+
# # ┌─────┬─────┬─────┐
|
|
2925
|
+
# # │ a ┆ b ┆ c │
|
|
2926
|
+
# # │ --- ┆ --- ┆ --- │
|
|
2927
|
+
# # │ str ┆ i64 ┆ f64 │
|
|
2928
|
+
# # ╞═════╪═════╪═════╡
|
|
2929
|
+
# # │ a ┆ 0 ┆ 4.0 │
|
|
2930
|
+
# # │ b ┆ 1 ┆ 3.0 │
|
|
2931
|
+
# # │ c ┆ 1 ┆ 1.0 │
|
|
2932
|
+
# # └─────┴─────┴─────┘
|
|
2933
|
+
#
|
|
2934
|
+
# @example The `GroupBy` object returned by this method is iterable, returning the name and data of each group.
|
|
2935
|
+
# df.group_by("a").each do |name, data|
|
|
2936
|
+
# # ...
|
|
2937
|
+
# end
|
|
2938
|
+
def group_by(*by, maintain_order: false, **named_by)
|
|
2939
|
+
named_by.each do |_, value|
|
|
2940
|
+
if !(value.is_a?(::String) || value.is_a?(Expr) || value.is_a?(Series))
|
|
2941
|
+
msg = "Expected Polars expression or object convertible to one, got #{value.class.name}."
|
|
2942
|
+
raise TypeError, msg
|
|
2943
|
+
end
|
|
1746
2944
|
end
|
|
1747
2945
|
GroupBy.new(
|
|
1748
2946
|
self,
|
|
1749
|
-
by,
|
|
1750
|
-
|
|
2947
|
+
*by,
|
|
2948
|
+
**named_by,
|
|
2949
|
+
maintain_order: maintain_order,
|
|
2950
|
+
predicates: nil
|
|
1751
2951
|
)
|
|
1752
2952
|
end
|
|
1753
|
-
alias_method :groupby, :group_by
|
|
1754
|
-
alias_method :group, :group_by
|
|
1755
2953
|
|
|
1756
2954
|
# Create rolling groups based on a time column.
|
|
1757
2955
|
#
|
|
1758
|
-
# Also works for index values of type `:i32` or `:i64`.
|
|
1759
|
-
#
|
|
1760
2956
|
# Different from a `dynamic_group_by` the windows are now determined by the
|
|
1761
2957
|
# individual values and are not of constant intervals. For constant intervals use
|
|
1762
2958
|
# *group_by_dynamic*
|
|
@@ -1790,16 +2986,16 @@ module Polars
|
|
|
1790
2986
|
# This column must be sorted in ascending order. If not the output will not
|
|
1791
2987
|
# make sense.
|
|
1792
2988
|
#
|
|
1793
|
-
# In case of a rolling
|
|
1794
|
-
#
|
|
1795
|
-
# performance matters use an
|
|
2989
|
+
# In case of a rolling operation on indices, dtype needs to be one of
|
|
2990
|
+
# \\\\{UInt32, UInt64, Int32, Int64}. Note that the first three get temporarily
|
|
2991
|
+
# cast to Int64, so if performance matters use an Int64 column.
|
|
1796
2992
|
# @param period [Object]
|
|
1797
2993
|
# Length of the window.
|
|
1798
2994
|
# @param offset [Object]
|
|
1799
2995
|
# Offset of the window. Default is -period.
|
|
1800
2996
|
# @param closed ["right", "left", "both", "none"]
|
|
1801
2997
|
# Define whether the temporal window interval is closed or not.
|
|
1802
|
-
# @param
|
|
2998
|
+
# @param group_by [Object]
|
|
1803
2999
|
# Also group by this column/these columns.
|
|
1804
3000
|
#
|
|
1805
3001
|
# @return [RollingGroupBy]
|
|
@@ -1813,7 +3009,7 @@ module Polars
|
|
|
1813
3009
|
# "2020-01-03 19:45:32",
|
|
1814
3010
|
# "2020-01-08 23:16:43"
|
|
1815
3011
|
# ]
|
|
1816
|
-
# df = Polars::DataFrame.new({"dt" => dates, "a" => [3, 7, 5, 9, 2, 1]}).
|
|
3012
|
+
# df = Polars::DataFrame.new({"dt" => dates, "a" => [3, 7, 5, 9, 2, 1]}).with_columns(
|
|
1817
3013
|
# Polars.col("dt").str.strptime(Polars::Datetime).set_sorted
|
|
1818
3014
|
# )
|
|
1819
3015
|
# df.rolling(index_column: "dt", period: "2d").agg(
|
|
@@ -1842,14 +3038,12 @@ module Polars
|
|
|
1842
3038
|
period:,
|
|
1843
3039
|
offset: nil,
|
|
1844
3040
|
closed: "right",
|
|
1845
|
-
|
|
3041
|
+
group_by: nil
|
|
1846
3042
|
)
|
|
1847
|
-
RollingGroupBy.new(self, index_column, period, offset, closed,
|
|
3043
|
+
RollingGroupBy.new(self, index_column, period, offset, closed, group_by, nil)
|
|
1848
3044
|
end
|
|
1849
|
-
alias_method :groupby_rolling, :rolling
|
|
1850
|
-
alias_method :group_by_rolling, :rolling
|
|
1851
3045
|
|
|
1852
|
-
# Group based on a time value (or index value of type
|
|
3046
|
+
# Group based on a time value (or index value of type Int32, Int64).
|
|
1853
3047
|
#
|
|
1854
3048
|
# Time windows are calculated and rows are assigned to windows. Different from a
|
|
1855
3049
|
# normal group by is that a row can be member of multiple groups. The time/index
|
|
@@ -1892,25 +3086,47 @@ module Polars
|
|
|
1892
3086
|
# make sense.
|
|
1893
3087
|
#
|
|
1894
3088
|
# In case of a dynamic group by on indices, dtype needs to be one of
|
|
1895
|
-
#
|
|
1896
|
-
# performance matters use an
|
|
3089
|
+
# \\\\{Int32, Int64}. Note that Int32 gets temporarily cast to Int64, so if
|
|
3090
|
+
# performance matters use an Int64 column.
|
|
1897
3091
|
# @param every
|
|
1898
3092
|
# Interval of the window.
|
|
1899
3093
|
# @param period
|
|
1900
|
-
# Length of the window, if
|
|
3094
|
+
# Length of the window, if nil it is equal to 'every'.
|
|
1901
3095
|
# @param offset
|
|
1902
|
-
# Offset of the window if
|
|
3096
|
+
# Offset of the window if nil and period is nil it will be equal to negative
|
|
1903
3097
|
# `every`.
|
|
1904
|
-
# @param truncate
|
|
1905
|
-
# Truncate the time value to the window lower bound.
|
|
1906
3098
|
# @param include_boundaries
|
|
1907
3099
|
# Add the lower and upper bound of the window to the "_lower_bound" and
|
|
1908
3100
|
# "_upper_bound" columns. This will impact performance because it's harder to
|
|
1909
3101
|
# parallelize
|
|
1910
3102
|
# @param closed ["right", "left", "both", "none"]
|
|
1911
3103
|
# Define whether the temporal window interval is closed or not.
|
|
1912
|
-
# @param
|
|
3104
|
+
# @param label ['left', 'right', 'datapoint']
|
|
3105
|
+
# Define which label to use for the window:
|
|
3106
|
+
#
|
|
3107
|
+
# - 'left': lower boundary of the window
|
|
3108
|
+
# - 'right': upper boundary of the window
|
|
3109
|
+
# - 'datapoint': the first value of the index column in the given window.
|
|
3110
|
+
# If you don't need the label to be at one of the boundaries, choose this
|
|
3111
|
+
# option for maximum performance
|
|
3112
|
+
# @param group_by
|
|
1913
3113
|
# Also group by this column/these columns
|
|
3114
|
+
# @param start_by ['window', 'datapoint', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']
|
|
3115
|
+
# The strategy to determine the start of the first window by.
|
|
3116
|
+
#
|
|
3117
|
+
# * 'window': Start by taking the earliest timestamp, truncating it with
|
|
3118
|
+
# `every`, and then adding `offset`.
|
|
3119
|
+
# Note that weekly windows start on Monday.
|
|
3120
|
+
# * 'datapoint': Start from the first encountered data point.
|
|
3121
|
+
# * a day of the week (only takes effect if `every` contains `'w'`):
|
|
3122
|
+
#
|
|
3123
|
+
# * 'monday': Start the window on the Monday before the first data point.
|
|
3124
|
+
# * 'tuesday': Start the window on the Tuesday before the first data point.
|
|
3125
|
+
# * ...
|
|
3126
|
+
# * 'sunday': Start the window on the Sunday before the first data point.
|
|
3127
|
+
#
|
|
3128
|
+
# The resulting window is then shifted back until the earliest datapoint
|
|
3129
|
+
# is in or in front of it.
|
|
1914
3130
|
#
|
|
1915
3131
|
# @return [DataFrame]
|
|
1916
3132
|
#
|
|
@@ -2035,7 +3251,7 @@ module Polars
|
|
|
2035
3251
|
# "time",
|
|
2036
3252
|
# every: "1h",
|
|
2037
3253
|
# closed: "both",
|
|
2038
|
-
#
|
|
3254
|
+
# group_by: "groups",
|
|
2039
3255
|
# include_boundaries: true
|
|
2040
3256
|
# ).agg([Polars.col("time").count.alias("time_count")])
|
|
2041
3257
|
# # =>
|
|
@@ -2085,10 +3301,10 @@ module Polars
|
|
|
2085
3301
|
every:,
|
|
2086
3302
|
period: nil,
|
|
2087
3303
|
offset: nil,
|
|
2088
|
-
truncate: true,
|
|
2089
3304
|
include_boundaries: false,
|
|
2090
3305
|
closed: "left",
|
|
2091
|
-
|
|
3306
|
+
label: "left",
|
|
3307
|
+
group_by: nil,
|
|
2092
3308
|
start_by: "window"
|
|
2093
3309
|
)
|
|
2094
3310
|
DynamicGroupBy.new(
|
|
@@ -2097,14 +3313,14 @@ module Polars
|
|
|
2097
3313
|
every,
|
|
2098
3314
|
period,
|
|
2099
3315
|
offset,
|
|
2100
|
-
truncate,
|
|
2101
3316
|
include_boundaries,
|
|
2102
3317
|
closed,
|
|
2103
|
-
|
|
2104
|
-
|
|
3318
|
+
label,
|
|
3319
|
+
group_by,
|
|
3320
|
+
start_by,
|
|
3321
|
+
nil
|
|
2105
3322
|
)
|
|
2106
3323
|
end
|
|
2107
|
-
alias_method :groupby_dynamic, :group_by_dynamic
|
|
2108
3324
|
|
|
2109
3325
|
# Upsample a DataFrame at a regular frequency.
|
|
2110
3326
|
#
|
|
@@ -2113,7 +3329,7 @@ module Polars
|
|
|
2113
3329
|
# Note that this column has to be sorted for the output to make sense.
|
|
2114
3330
|
# @param every [String]
|
|
2115
3331
|
# interval will start 'every' duration
|
|
2116
|
-
# @param
|
|
3332
|
+
# @param group_by [Object]
|
|
2117
3333
|
# First group by these columns and then upsample for every group
|
|
2118
3334
|
# @param maintain_order [Boolean]
|
|
2119
3335
|
# Keep the ordering predictable. This is slower.
|
|
@@ -2152,7 +3368,7 @@ module Polars
|
|
|
2152
3368
|
# }
|
|
2153
3369
|
# ).set_sorted("time")
|
|
2154
3370
|
# df.upsample(
|
|
2155
|
-
# time_column: "time", every: "1mo",
|
|
3371
|
+
# time_column: "time", every: "1mo", group_by: "groups", maintain_order: true
|
|
2156
3372
|
# ).select(Polars.all.forward_fill)
|
|
2157
3373
|
# # =>
|
|
2158
3374
|
# # shape: (7, 3)
|
|
@@ -2172,20 +3388,20 @@ module Polars
|
|
|
2172
3388
|
def upsample(
|
|
2173
3389
|
time_column:,
|
|
2174
3390
|
every:,
|
|
2175
|
-
|
|
3391
|
+
group_by: nil,
|
|
2176
3392
|
maintain_order: false
|
|
2177
3393
|
)
|
|
2178
|
-
if
|
|
2179
|
-
|
|
3394
|
+
if group_by.nil?
|
|
3395
|
+
group_by = []
|
|
2180
3396
|
end
|
|
2181
|
-
if
|
|
2182
|
-
|
|
3397
|
+
if group_by.is_a?(::String)
|
|
3398
|
+
group_by = [group_by]
|
|
2183
3399
|
end
|
|
2184
3400
|
|
|
2185
3401
|
every = Utils.parse_as_duration_string(every)
|
|
2186
3402
|
|
|
2187
3403
|
_from_rbdf(
|
|
2188
|
-
_df.upsample(
|
|
3404
|
+
_df.upsample(group_by, time_column, every, maintain_order)
|
|
2189
3405
|
)
|
|
2190
3406
|
end
|
|
2191
3407
|
|
|
@@ -2211,14 +3427,14 @@ module Polars
|
|
|
2211
3427
|
# Join column of the right DataFrame.
|
|
2212
3428
|
# @param on [String]
|
|
2213
3429
|
# Join column of both DataFrames. If set, `left_on` and `right_on` should be
|
|
2214
|
-
#
|
|
2215
|
-
# @param by [Object]
|
|
2216
|
-
# join on these columns before doing asof join
|
|
3430
|
+
# nil.
|
|
2217
3431
|
# @param by_left [Object]
|
|
2218
3432
|
# join on these columns before doing asof join
|
|
2219
3433
|
# @param by_right [Object]
|
|
2220
3434
|
# join on these columns before doing asof join
|
|
2221
|
-
# @param
|
|
3435
|
+
# @param by [Object]
|
|
3436
|
+
# join on these columns before doing asof join
|
|
3437
|
+
# @param strategy ["backward", "forward"]
|
|
2222
3438
|
# Join strategy.
|
|
2223
3439
|
# @param suffix [String]
|
|
2224
3440
|
# Suffix to append to columns with a duplicate name.
|
|
@@ -2254,6 +3470,14 @@ module Polars
|
|
|
2254
3470
|
# - true: -> Always coalesce join columns.
|
|
2255
3471
|
# - false: -> Never coalesce join columns.
|
|
2256
3472
|
# Note that joining on any other expressions than `col` will turn off coalescing.
|
|
3473
|
+
# @param allow_exact_matches [Boolean]
|
|
3474
|
+
# Whether exact matches are valid join predicates.
|
|
3475
|
+
# - If true, allow matching with the same `on` value (i.e. less-than-or-equal-to / greater-than-or-equal-to).
|
|
3476
|
+
# - If false, don't match the same `on` value (i.e., strictly less-than / strictly greater-than).
|
|
3477
|
+
# @param check_sortedness [Boolean]
|
|
3478
|
+
# Check the sortedness of the asof keys. If the keys are not sorted Polars
|
|
3479
|
+
# will error, or in case of 'by' argument raise a warning. This might become
|
|
3480
|
+
# a hard error in the future.
|
|
2257
3481
|
#
|
|
2258
3482
|
# @return [DataFrame]
|
|
2259
3483
|
#
|
|
@@ -2308,7 +3532,9 @@ module Polars
|
|
|
2308
3532
|
tolerance: nil,
|
|
2309
3533
|
allow_parallel: true,
|
|
2310
3534
|
force_parallel: false,
|
|
2311
|
-
coalesce: true
|
|
3535
|
+
coalesce: true,
|
|
3536
|
+
allow_exact_matches: true,
|
|
3537
|
+
check_sortedness: true
|
|
2312
3538
|
)
|
|
2313
3539
|
lazy
|
|
2314
3540
|
.join_asof(
|
|
@@ -2324,9 +3550,11 @@ module Polars
|
|
|
2324
3550
|
tolerance: tolerance,
|
|
2325
3551
|
allow_parallel: allow_parallel,
|
|
2326
3552
|
force_parallel: force_parallel,
|
|
2327
|
-
coalesce: coalesce
|
|
3553
|
+
coalesce: coalesce,
|
|
3554
|
+
allow_exact_matches: allow_exact_matches,
|
|
3555
|
+
check_sortedness: check_sortedness
|
|
2328
3556
|
)
|
|
2329
|
-
.collect(
|
|
3557
|
+
.collect(optimizations: QueryOptFlags._eager)
|
|
2330
3558
|
end
|
|
2331
3559
|
|
|
2332
3560
|
# Join in SQL-like fashion.
|
|
@@ -2349,7 +3577,7 @@ module Polars
|
|
|
2349
3577
|
# * *one_to_one* - “1:1”: check if join keys are unique in both left and right datasets
|
|
2350
3578
|
# * *one_to_many* - “1:m”: check if join keys are unique in left dataset
|
|
2351
3579
|
# * *many_to_one* - “m:1”: check if join keys are unique in right dataset
|
|
2352
|
-
# @param
|
|
3580
|
+
# @param nulls_equal [Boolean]
|
|
2353
3581
|
# Join on null values. By default null values will never produce matches.
|
|
2354
3582
|
# @param coalesce [Boolean]
|
|
2355
3583
|
# Coalescing behavior (merging of join columns).
|
|
@@ -2357,6 +3585,24 @@ module Polars
|
|
|
2357
3585
|
# - true: -> Always coalesce join columns.
|
|
2358
3586
|
# - false: -> Never coalesce join columns.
|
|
2359
3587
|
# Note that joining on any other expressions than `col` will turn off coalescing.
|
|
3588
|
+
# @param maintain_order ['none', 'left', 'right', 'left_right', 'right_left']
|
|
3589
|
+
# Which DataFrame row order to preserve, if any.
|
|
3590
|
+
# Do not rely on any observed ordering without explicitly
|
|
3591
|
+
# setting this parameter, as your code may break in a future release.
|
|
3592
|
+
# Not specifying any ordering can improve performance
|
|
3593
|
+
# Supported for inner, left, right and full joins
|
|
3594
|
+
#
|
|
3595
|
+
# * *none*
|
|
3596
|
+
# No specific ordering is desired. The ordering might differ across
|
|
3597
|
+
# Polars versions or even between different runs.
|
|
3598
|
+
# * *left*
|
|
3599
|
+
# Preserves the order of the left DataFrame.
|
|
3600
|
+
# * *right*
|
|
3601
|
+
# Preserves the order of the right DataFrame.
|
|
3602
|
+
# * *left_right*
|
|
3603
|
+
# First preserves the order of the left DataFrame, then the right.
|
|
3604
|
+
# * *right_left*
|
|
3605
|
+
# First preserves the order of the right DataFrame, then the left.
|
|
2360
3606
|
#
|
|
2361
3607
|
# @return [DataFrame]
|
|
2362
3608
|
#
|
|
@@ -2439,15 +3685,17 @@ module Polars
|
|
|
2439
3685
|
# # ╞═════╪═════╪═════╡
|
|
2440
3686
|
# # │ 3 ┆ 8.0 ┆ c │
|
|
2441
3687
|
# # └─────┴─────┴─────┘
|
|
2442
|
-
def join(
|
|
3688
|
+
def join(
|
|
3689
|
+
other,
|
|
2443
3690
|
left_on: nil,
|
|
2444
3691
|
right_on: nil,
|
|
2445
3692
|
on: nil,
|
|
2446
3693
|
how: "inner",
|
|
2447
3694
|
suffix: "_right",
|
|
2448
3695
|
validate: "m:m",
|
|
2449
|
-
|
|
2450
|
-
coalesce: nil
|
|
3696
|
+
nulls_equal: false,
|
|
3697
|
+
coalesce: nil,
|
|
3698
|
+
maintain_order: nil
|
|
2451
3699
|
)
|
|
2452
3700
|
lazy
|
|
2453
3701
|
.join(
|
|
@@ -2458,10 +3706,157 @@ module Polars
|
|
|
2458
3706
|
how: how,
|
|
2459
3707
|
suffix: suffix,
|
|
2460
3708
|
validate: validate,
|
|
2461
|
-
|
|
2462
|
-
coalesce: coalesce
|
|
3709
|
+
nulls_equal: nulls_equal,
|
|
3710
|
+
coalesce: coalesce,
|
|
3711
|
+
maintain_order: maintain_order
|
|
2463
3712
|
)
|
|
2464
|
-
.collect(
|
|
3713
|
+
.collect(optimizations: QueryOptFlags._eager)
|
|
3714
|
+
end
|
|
3715
|
+
|
|
3716
|
+
# Perform a join based on one or multiple (in)equality predicates.
|
|
3717
|
+
#
|
|
3718
|
+
# This performs an inner join, so only rows where all predicates are true
|
|
3719
|
+
# are included in the result, and a row from either DataFrame may be included
|
|
3720
|
+
# multiple times in the result.
|
|
3721
|
+
#
|
|
3722
|
+
# @note
|
|
3723
|
+
# The row order of the input DataFrames is not preserved.
|
|
3724
|
+
#
|
|
3725
|
+
# @note
|
|
3726
|
+
# This functionality is experimental. It may be
|
|
3727
|
+
# changed at any point without it being considered a breaking change.
|
|
3728
|
+
#
|
|
3729
|
+
# @param other [DataFrame]
|
|
3730
|
+
# DataFrame to join with.
|
|
3731
|
+
# @param predicates [Array]
|
|
3732
|
+
# (In)Equality condition to join the two tables on.
|
|
3733
|
+
# When a column name occurs in both tables, the proper suffix must
|
|
3734
|
+
# be applied in the predicate.
|
|
3735
|
+
# @param suffix [String]
|
|
3736
|
+
# Suffix to append to columns with a duplicate name.
|
|
3737
|
+
#
|
|
3738
|
+
# @return [DataFrame]
|
|
3739
|
+
#
|
|
3740
|
+
# @example Join two dataframes together based on two predicates which get AND-ed together.
|
|
3741
|
+
# east = Polars::DataFrame.new(
|
|
3742
|
+
# {
|
|
3743
|
+
# "id": [100, 101, 102],
|
|
3744
|
+
# "dur": [120, 140, 160],
|
|
3745
|
+
# "rev": [12, 14, 16],
|
|
3746
|
+
# "cores": [2, 8, 4]
|
|
3747
|
+
# }
|
|
3748
|
+
# )
|
|
3749
|
+
# west = Polars::DataFrame.new(
|
|
3750
|
+
# {
|
|
3751
|
+
# "t_id": [404, 498, 676, 742],
|
|
3752
|
+
# "time": [90, 130, 150, 170],
|
|
3753
|
+
# "cost": [9, 13, 15, 16],
|
|
3754
|
+
# "cores": [4, 2, 1, 4]
|
|
3755
|
+
# }
|
|
3756
|
+
# )
|
|
3757
|
+
# east.join_where(
|
|
3758
|
+
# west,
|
|
3759
|
+
# Polars.col("dur") < Polars.col("time"),
|
|
3760
|
+
# Polars.col("rev") < Polars.col("cost")
|
|
3761
|
+
# )
|
|
3762
|
+
# # =>
|
|
3763
|
+
# # shape: (5, 8)
|
|
3764
|
+
# # ┌─────┬─────┬─────┬───────┬──────┬──────┬──────┬─────────────┐
|
|
3765
|
+
# # │ id ┆ dur ┆ rev ┆ cores ┆ t_id ┆ time ┆ cost ┆ cores_right │
|
|
3766
|
+
# # │ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
|
|
3767
|
+
# # │ i64 ┆ i64 ┆ i64 ┆ i64 ┆ i64 ┆ i64 ┆ i64 ┆ i64 │
|
|
3768
|
+
# # ╞═════╪═════╪═════╪═══════╪══════╪══════╪══════╪═════════════╡
|
|
3769
|
+
# # │ 100 ┆ 120 ┆ 12 ┆ 2 ┆ 498 ┆ 130 ┆ 13 ┆ 2 │
|
|
3770
|
+
# # │ 100 ┆ 120 ┆ 12 ┆ 2 ┆ 676 ┆ 150 ┆ 15 ┆ 1 │
|
|
3771
|
+
# # │ 100 ┆ 120 ┆ 12 ┆ 2 ┆ 742 ┆ 170 ┆ 16 ┆ 4 │
|
|
3772
|
+
# # │ 101 ┆ 140 ┆ 14 ┆ 8 ┆ 676 ┆ 150 ┆ 15 ┆ 1 │
|
|
3773
|
+
# # │ 101 ┆ 140 ┆ 14 ┆ 8 ┆ 742 ┆ 170 ┆ 16 ┆ 4 │
|
|
3774
|
+
# # └─────┴─────┴─────┴───────┴──────┴──────┴──────┴─────────────┘
|
|
3775
|
+
#
|
|
3776
|
+
# @example To OR them together, use a single expression and the `|` operator.
|
|
3777
|
+
# east.join_where(
|
|
3778
|
+
# west,
|
|
3779
|
+
# (Polars.col("dur") < Polars.col("time")) | (Polars.col("rev") < Polars.col("cost"))
|
|
3780
|
+
# )
|
|
3781
|
+
# # =>
|
|
3782
|
+
# # shape: (6, 8)
|
|
3783
|
+
# # ┌─────┬─────┬─────┬───────┬──────┬──────┬──────┬─────────────┐
|
|
3784
|
+
# # │ id ┆ dur ┆ rev ┆ cores ┆ t_id ┆ time ┆ cost ┆ cores_right │
|
|
3785
|
+
# # │ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
|
|
3786
|
+
# # │ i64 ┆ i64 ┆ i64 ┆ i64 ┆ i64 ┆ i64 ┆ i64 ┆ i64 │
|
|
3787
|
+
# # ╞═════╪═════╪═════╪═══════╪══════╪══════╪══════╪═════════════╡
|
|
3788
|
+
# # │ 100 ┆ 120 ┆ 12 ┆ 2 ┆ 498 ┆ 130 ┆ 13 ┆ 2 │
|
|
3789
|
+
# # │ 100 ┆ 120 ┆ 12 ┆ 2 ┆ 676 ┆ 150 ┆ 15 ┆ 1 │
|
|
3790
|
+
# # │ 100 ┆ 120 ┆ 12 ┆ 2 ┆ 742 ┆ 170 ┆ 16 ┆ 4 │
|
|
3791
|
+
# # │ 101 ┆ 140 ┆ 14 ┆ 8 ┆ 676 ┆ 150 ┆ 15 ┆ 1 │
|
|
3792
|
+
# # │ 101 ┆ 140 ┆ 14 ┆ 8 ┆ 742 ┆ 170 ┆ 16 ┆ 4 │
|
|
3793
|
+
# # │ 102 ┆ 160 ┆ 16 ┆ 4 ┆ 742 ┆ 170 ┆ 16 ┆ 4 │
|
|
3794
|
+
# # └─────┴─────┴─────┴───────┴──────┴──────┴──────┴─────────────┘
|
|
3795
|
+
def join_where(
|
|
3796
|
+
other,
|
|
3797
|
+
*predicates,
|
|
3798
|
+
suffix: "_right"
|
|
3799
|
+
)
|
|
3800
|
+
Utils.require_same_type(self, other)
|
|
3801
|
+
|
|
3802
|
+
lazy
|
|
3803
|
+
.join_where(
|
|
3804
|
+
other.lazy,
|
|
3805
|
+
*predicates,
|
|
3806
|
+
suffix: suffix
|
|
3807
|
+
)
|
|
3808
|
+
.collect(optimizations: QueryOptFlags._eager)
|
|
3809
|
+
end
|
|
3810
|
+
|
|
3811
|
+
# Selects rows from this DataFrame at the given indices.
|
|
3812
|
+
#
|
|
3813
|
+
# @note
|
|
3814
|
+
# This functionality is experimental. It may be
|
|
3815
|
+
# changed at any point without it being considered a breaking change.
|
|
3816
|
+
#
|
|
3817
|
+
# @param indices [Object]
|
|
3818
|
+
# The indices of the rows to select.
|
|
3819
|
+
# @param null_on_oob [Boolean]
|
|
3820
|
+
# If true when an index is out-of-bounds a null row will be generated
|
|
3821
|
+
# instead of raising an error.
|
|
3822
|
+
#
|
|
3823
|
+
# @return [DataFrame]
|
|
3824
|
+
#
|
|
3825
|
+
# @example
|
|
3826
|
+
# df = Polars::DataFrame.new({"x" => [2, 1, 0], "s" => ["foo", "bar", "baz"]})
|
|
3827
|
+
# df.gather([2, 0, 0])
|
|
3828
|
+
# # =>
|
|
3829
|
+
# # shape: (3, 2)
|
|
3830
|
+
# # ┌─────┬─────┐
|
|
3831
|
+
# # │ x ┆ s │
|
|
3832
|
+
# # │ --- ┆ --- │
|
|
3833
|
+
# # │ i64 ┆ str │
|
|
3834
|
+
# # ╞═════╪═════╡
|
|
3835
|
+
# # │ 0 ┆ baz │
|
|
3836
|
+
# # │ 2 ┆ foo │
|
|
3837
|
+
# # │ 2 ┆ foo │
|
|
3838
|
+
# # └─────┴─────┘
|
|
3839
|
+
#
|
|
3840
|
+
# @example
|
|
3841
|
+
# df.gather([0, 10, 1], null_on_oob: true)
|
|
3842
|
+
# # =>
|
|
3843
|
+
# # shape: (3, 2)
|
|
3844
|
+
# # ┌──────┬──────┐
|
|
3845
|
+
# # │ x ┆ s │
|
|
3846
|
+
# # │ --- ┆ --- │
|
|
3847
|
+
# # │ i64 ┆ str │
|
|
3848
|
+
# # ╞══════╪══════╡
|
|
3849
|
+
# # │ 2 ┆ foo │
|
|
3850
|
+
# # │ null ┆ null │
|
|
3851
|
+
# # │ 1 ┆ bar │
|
|
3852
|
+
# # └──────┴──────┘
|
|
3853
|
+
def gather(
|
|
3854
|
+
indices,
|
|
3855
|
+
null_on_oob: false
|
|
3856
|
+
)
|
|
3857
|
+
lazy
|
|
3858
|
+
.gather(indices, null_on_oob: null_on_oob)
|
|
3859
|
+
.collect(optimizations: QueryOptFlags._eager)
|
|
2465
3860
|
end
|
|
2466
3861
|
|
|
2467
3862
|
# Apply a custom/user-defined function (UDF) over the rows of the DataFrame.
|
|
@@ -2524,61 +3919,14 @@ module Polars
|
|
|
2524
3919
|
# # │ 9 │
|
|
2525
3920
|
# # │ 14 │
|
|
2526
3921
|
# # └─────┘
|
|
2527
|
-
def map_rows(return_dtype: nil, inference_size: 256, &
|
|
2528
|
-
out, is_df = _df.map_rows(
|
|
3922
|
+
def map_rows(return_dtype: nil, inference_size: 256, &function)
|
|
3923
|
+
out, is_df = _df.map_rows(function, return_dtype, inference_size)
|
|
2529
3924
|
if is_df
|
|
2530
3925
|
_from_rbdf(out)
|
|
2531
3926
|
else
|
|
2532
3927
|
_from_rbdf(Utils.wrap_s(out).to_frame._df)
|
|
2533
3928
|
end
|
|
2534
3929
|
end
|
|
2535
|
-
alias_method :apply, :map_rows
|
|
2536
|
-
|
|
2537
|
-
# Return a new DataFrame with the column added or replaced.
|
|
2538
|
-
#
|
|
2539
|
-
# @param column [Object]
|
|
2540
|
-
# Series, where the name of the Series refers to the column in the DataFrame.
|
|
2541
|
-
#
|
|
2542
|
-
# @return [DataFrame]
|
|
2543
|
-
#
|
|
2544
|
-
# @example Added
|
|
2545
|
-
# df = Polars::DataFrame.new(
|
|
2546
|
-
# {
|
|
2547
|
-
# "a" => [1, 3, 5],
|
|
2548
|
-
# "b" => [2, 4, 6]
|
|
2549
|
-
# }
|
|
2550
|
-
# )
|
|
2551
|
-
# df.with_column((Polars.col("b") ** 2).alias("b_squared"))
|
|
2552
|
-
# # =>
|
|
2553
|
-
# # shape: (3, 3)
|
|
2554
|
-
# # ┌─────┬─────┬───────────┐
|
|
2555
|
-
# # │ a ┆ b ┆ b_squared │
|
|
2556
|
-
# # │ --- ┆ --- ┆ --- │
|
|
2557
|
-
# # │ i64 ┆ i64 ┆ i64 │
|
|
2558
|
-
# # ╞═════╪═════╪═══════════╡
|
|
2559
|
-
# # │ 1 ┆ 2 ┆ 4 │
|
|
2560
|
-
# # │ 3 ┆ 4 ┆ 16 │
|
|
2561
|
-
# # │ 5 ┆ 6 ┆ 36 │
|
|
2562
|
-
# # └─────┴─────┴───────────┘
|
|
2563
|
-
#
|
|
2564
|
-
# @example Replaced
|
|
2565
|
-
# df.with_column(Polars.col("a") ** 2)
|
|
2566
|
-
# # =>
|
|
2567
|
-
# # shape: (3, 2)
|
|
2568
|
-
# # ┌─────┬─────┐
|
|
2569
|
-
# # │ a ┆ b │
|
|
2570
|
-
# # │ --- ┆ --- │
|
|
2571
|
-
# # │ i64 ┆ i64 │
|
|
2572
|
-
# # ╞═════╪═════╡
|
|
2573
|
-
# # │ 1 ┆ 2 │
|
|
2574
|
-
# # │ 9 ┆ 4 │
|
|
2575
|
-
# # │ 25 ┆ 6 │
|
|
2576
|
-
# # └─────┴─────┘
|
|
2577
|
-
def with_column(column)
|
|
2578
|
-
lazy
|
|
2579
|
-
.with_column(column)
|
|
2580
|
-
.collect(no_optimization: true, string_cache: false)
|
|
2581
|
-
end
|
|
2582
3930
|
|
|
2583
3931
|
# Return a new DataFrame grown horizontally by stacking multiple Series to it.
|
|
2584
3932
|
#
|
|
@@ -2624,7 +3972,7 @@ module Polars
|
|
|
2624
3972
|
|
|
2625
3973
|
# Grow this DataFrame vertically by stacking a DataFrame to it.
|
|
2626
3974
|
#
|
|
2627
|
-
# @param
|
|
3975
|
+
# @param other [DataFrame]
|
|
2628
3976
|
# DataFrame to stack.
|
|
2629
3977
|
# @param in_place [Boolean]
|
|
2630
3978
|
# Modify in place
|
|
@@ -2659,12 +4007,12 @@ module Polars
|
|
|
2659
4007
|
# # │ 3 ┆ 8 ┆ c │
|
|
2660
4008
|
# # │ 4 ┆ 9 ┆ d │
|
|
2661
4009
|
# # └─────┴─────┴─────┘
|
|
2662
|
-
def vstack(
|
|
4010
|
+
def vstack(other, in_place: false)
|
|
2663
4011
|
if in_place
|
|
2664
|
-
_df.vstack_mut(
|
|
4012
|
+
_df.vstack_mut(other._df)
|
|
2665
4013
|
self
|
|
2666
4014
|
else
|
|
2667
|
-
_from_rbdf(_df.vstack(
|
|
4015
|
+
_from_rbdf(_df.vstack(other._df))
|
|
2668
4016
|
end
|
|
2669
4017
|
end
|
|
2670
4018
|
|
|
@@ -2717,6 +4065,9 @@ module Polars
|
|
|
2717
4065
|
#
|
|
2718
4066
|
# @param columns [Object]
|
|
2719
4067
|
# Column(s) to drop.
|
|
4068
|
+
# @param strict [Boolean]
|
|
4069
|
+
# Validate that all column names exist in the current schema,
|
|
4070
|
+
# and throw an exception if any do not.
|
|
2720
4071
|
#
|
|
2721
4072
|
# @return [DataFrame]
|
|
2722
4073
|
#
|
|
@@ -2768,8 +4119,8 @@ module Polars
|
|
|
2768
4119
|
# # │ 7.0 │
|
|
2769
4120
|
# # │ 8.0 │
|
|
2770
4121
|
# # └─────┘
|
|
2771
|
-
def drop(*columns)
|
|
2772
|
-
lazy.drop(*columns).collect(
|
|
4122
|
+
def drop(*columns, strict: true)
|
|
4123
|
+
lazy.drop(*columns, strict: strict).collect(optimizations: QueryOptFlags._eager)
|
|
2773
4124
|
end
|
|
2774
4125
|
|
|
2775
4126
|
# Drop in place.
|
|
@@ -2882,7 +4233,7 @@ module Polars
|
|
|
2882
4233
|
# df.cast(Polars::String).to_h(as_series: false)
|
|
2883
4234
|
# # => {"foo"=>["1", "2", "3"], "bar"=>["6.0", "7.0", "8.0"], "ham"=>["2020-01-02", "2021-03-04", "2022-05-06"]}
|
|
2884
4235
|
def cast(dtypes, strict: true)
|
|
2885
|
-
lazy.cast(dtypes, strict: strict).collect(
|
|
4236
|
+
lazy.cast(dtypes, strict: strict).collect(optimizations: QueryOptFlags._eager)
|
|
2886
4237
|
end
|
|
2887
4238
|
|
|
2888
4239
|
# Create an empty copy of the current DataFrame.
|
|
@@ -2932,7 +4283,6 @@ module Polars
|
|
|
2932
4283
|
clone
|
|
2933
4284
|
end
|
|
2934
4285
|
end
|
|
2935
|
-
alias_method :cleared, :clear
|
|
2936
4286
|
|
|
2937
4287
|
# clone handled by initialize_copy
|
|
2938
4288
|
|
|
@@ -2994,10 +4344,13 @@ module Polars
|
|
|
2994
4344
|
_df.get_columns.map { |s| Utils.wrap_s(s) }
|
|
2995
4345
|
end
|
|
2996
4346
|
|
|
2997
|
-
# Get a single column
|
|
4347
|
+
# Get a single column by name.
|
|
2998
4348
|
#
|
|
2999
4349
|
# @param name [String]
|
|
3000
4350
|
# Name of the column to retrieve.
|
|
4351
|
+
# @param default [Object]
|
|
4352
|
+
# Value to return if the column does not exist; if not explicitly set and
|
|
4353
|
+
# the column is not present a `ColumnNotFoundError` exception is raised.
|
|
3001
4354
|
#
|
|
3002
4355
|
# @return [Series]
|
|
3003
4356
|
#
|
|
@@ -3012,8 +4365,22 @@ module Polars
|
|
|
3012
4365
|
# # 2
|
|
3013
4366
|
# # 3
|
|
3014
4367
|
# # ]
|
|
3015
|
-
|
|
3016
|
-
|
|
4368
|
+
#
|
|
4369
|
+
# @example
|
|
4370
|
+
# df.get_column("baz", default: Polars::Series.new("baz", ["?", "?", "?"]))
|
|
4371
|
+
# # =>
|
|
4372
|
+
# # shape: (3,)
|
|
4373
|
+
# # Series: 'baz' [str]
|
|
4374
|
+
# # [
|
|
4375
|
+
# # "?"
|
|
4376
|
+
# # "?"
|
|
4377
|
+
# # "?"
|
|
4378
|
+
# # ]
|
|
4379
|
+
def get_column(name, default: NO_DEFAULT)
|
|
4380
|
+
Utils.wrap_s(_df.get_column(name.to_s))
|
|
4381
|
+
rescue ColumnNotFoundError
|
|
4382
|
+
raise if default.eql?(NO_DEFAULT)
|
|
4383
|
+
default
|
|
3017
4384
|
end
|
|
3018
4385
|
|
|
3019
4386
|
# Fill null values using the specified value or strategy.
|
|
@@ -3099,14 +4466,14 @@ module Polars
|
|
|
3099
4466
|
_from_rbdf(
|
|
3100
4467
|
lazy
|
|
3101
4468
|
.fill_null(value, strategy: strategy, limit: limit, matches_supertype: matches_supertype)
|
|
3102
|
-
.collect(
|
|
4469
|
+
.collect(optimizations: QueryOptFlags._eager)
|
|
3103
4470
|
._df
|
|
3104
4471
|
)
|
|
3105
4472
|
end
|
|
3106
4473
|
|
|
3107
4474
|
# Fill floating point NaN values by an Expression evaluation.
|
|
3108
4475
|
#
|
|
3109
|
-
# @param
|
|
4476
|
+
# @param value [Object]
|
|
3110
4477
|
# Value to fill NaN with.
|
|
3111
4478
|
#
|
|
3112
4479
|
# @return [DataFrame]
|
|
@@ -3135,14 +4502,20 @@ module Polars
|
|
|
3135
4502
|
# # │ 99.0 ┆ 99.0 │
|
|
3136
4503
|
# # │ 4.0 ┆ 13.0 │
|
|
3137
4504
|
# # └──────┴──────┘
|
|
3138
|
-
def fill_nan(
|
|
3139
|
-
lazy.fill_nan(
|
|
4505
|
+
def fill_nan(value)
|
|
4506
|
+
lazy.fill_nan(value).collect(optimizations: QueryOptFlags._eager)
|
|
3140
4507
|
end
|
|
3141
4508
|
|
|
3142
4509
|
# Explode `DataFrame` to long format by exploding a column with Lists.
|
|
3143
4510
|
#
|
|
3144
4511
|
# @param columns [Object]
|
|
3145
4512
|
# Column of LargeList type.
|
|
4513
|
+
# @param more_columns [Array]
|
|
4514
|
+
# Additional names of columns to explode, specified as positional arguments.
|
|
4515
|
+
# @param empty_as_null [Boolean]
|
|
4516
|
+
# Explode an empty list/array into a `null`.
|
|
4517
|
+
# @param keep_nulls [Boolean]
|
|
4518
|
+
# Explode a `null` list/array into a `null`.
|
|
3146
4519
|
#
|
|
3147
4520
|
# @return [DataFrame]
|
|
3148
4521
|
#
|
|
@@ -3170,25 +4543,43 @@ module Polars
|
|
|
3170
4543
|
# # │ c ┆ 7 │
|
|
3171
4544
|
# # │ c ┆ 8 │
|
|
3172
4545
|
# # └─────────┴─────────┘
|
|
3173
|
-
def explode(
|
|
3174
|
-
|
|
4546
|
+
def explode(
|
|
4547
|
+
columns,
|
|
4548
|
+
*more_columns,
|
|
4549
|
+
empty_as_null: true,
|
|
4550
|
+
keep_nulls: true
|
|
4551
|
+
)
|
|
4552
|
+
lazy
|
|
4553
|
+
.explode(
|
|
4554
|
+
columns,
|
|
4555
|
+
*more_columns,
|
|
4556
|
+
empty_as_null: empty_as_null,
|
|
4557
|
+
keep_nulls: keep_nulls
|
|
4558
|
+
).collect(optimizations: QueryOptFlags._eager)
|
|
3175
4559
|
end
|
|
3176
4560
|
|
|
3177
4561
|
# Create a spreadsheet-style pivot table as a DataFrame.
|
|
3178
4562
|
#
|
|
4563
|
+
# @param on [Object]
|
|
4564
|
+
# Columns whose values will be used as the header of the output DataFrame
|
|
4565
|
+
# @param on_columns [Object]
|
|
4566
|
+
# What value combinations will be considered for the output table.
|
|
4567
|
+
# @param index [Object]
|
|
4568
|
+
# One or multiple keys to group by
|
|
3179
4569
|
# @param values [Object]
|
|
3180
4570
|
# Column values to aggregate. Can be multiple columns if the *columns*
|
|
3181
4571
|
# arguments contains multiple columns as well
|
|
3182
|
-
# @param index [Object]
|
|
3183
|
-
# One or multiple keys to group by
|
|
3184
|
-
# @param on [Object]
|
|
3185
|
-
# Columns whose values will be used as the header of the output DataFrame
|
|
3186
4572
|
# @param aggregate_function ["first", "sum", "max", "min", "mean", "median", "last", "count"]
|
|
3187
4573
|
# A predefined aggregate function str or an expression.
|
|
3188
4574
|
# @param maintain_order [Object]
|
|
3189
4575
|
# Sort the grouped keys so that the output order is predictable.
|
|
3190
4576
|
# @param sort_columns [Object]
|
|
3191
4577
|
# Sort the transposed columns by name. Default is by order of discovery.
|
|
4578
|
+
# @param separator [String]
|
|
4579
|
+
# Used as separator/delimiter in generated column names in case of multiple
|
|
4580
|
+
# `values` columns.
|
|
4581
|
+
# @param column_naming ['auto', 'combine']
|
|
4582
|
+
# How resulting column names will be constructed.
|
|
3192
4583
|
#
|
|
3193
4584
|
# @return [DataFrame]
|
|
3194
4585
|
#
|
|
@@ -3213,60 +4604,37 @@ module Polars
|
|
|
3213
4604
|
# # └─────┴─────┴─────┘
|
|
3214
4605
|
def pivot(
|
|
3215
4606
|
on,
|
|
4607
|
+
on_columns: nil,
|
|
3216
4608
|
index: nil,
|
|
3217
4609
|
values: nil,
|
|
3218
4610
|
aggregate_function: nil,
|
|
3219
4611
|
maintain_order: true,
|
|
3220
4612
|
sort_columns: false,
|
|
3221
|
-
separator: "_"
|
|
4613
|
+
separator: "_",
|
|
4614
|
+
column_naming: "auto"
|
|
3222
4615
|
)
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
end
|
|
3228
|
-
|
|
3229
|
-
if aggregate_function.is_a?(::String)
|
|
3230
|
-
case aggregate_function
|
|
3231
|
-
when "first"
|
|
3232
|
-
aggregate_expr = F.element.first._rbexpr
|
|
3233
|
-
when "sum"
|
|
3234
|
-
aggregate_expr = F.element.sum._rbexpr
|
|
3235
|
-
when "max"
|
|
3236
|
-
aggregate_expr = F.element.max._rbexpr
|
|
3237
|
-
when "min"
|
|
3238
|
-
aggregate_expr = F.element.min._rbexpr
|
|
3239
|
-
when "mean"
|
|
3240
|
-
aggregate_expr = F.element.mean._rbexpr
|
|
3241
|
-
when "median"
|
|
3242
|
-
aggregate_expr = F.element.median._rbexpr
|
|
3243
|
-
when "last"
|
|
3244
|
-
aggregate_expr = F.element.last._rbexpr
|
|
3245
|
-
when "len"
|
|
3246
|
-
aggregate_expr = F.len._rbexpr
|
|
3247
|
-
when "count"
|
|
3248
|
-
warn "`aggregate_function: \"count\"` input for `pivot` is deprecated. Use `aggregate_function: \"len\"` instead."
|
|
3249
|
-
aggregate_expr = F.len._rbexpr
|
|
3250
|
-
else
|
|
3251
|
-
raise ArgumentError, "Argument aggregate fn: '#{aggregate_fn}' was not expected."
|
|
4616
|
+
if on_columns.nil?
|
|
4617
|
+
cols = select(on).unique(maintain_order: true)
|
|
4618
|
+
if sort_columns
|
|
4619
|
+
cols = cols.sort(on)
|
|
3252
4620
|
end
|
|
3253
|
-
|
|
3254
|
-
aggregate_expr = nil
|
|
4621
|
+
on_cols = cols
|
|
3255
4622
|
else
|
|
3256
|
-
|
|
4623
|
+
on_cols = on_columns
|
|
3257
4624
|
end
|
|
3258
4625
|
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
4626
|
+
lazy
|
|
4627
|
+
.pivot(
|
|
4628
|
+
on,
|
|
4629
|
+
on_columns: on_cols,
|
|
4630
|
+
index: index,
|
|
4631
|
+
values: values,
|
|
4632
|
+
aggregate_function: aggregate_function,
|
|
4633
|
+
maintain_order: maintain_order,
|
|
4634
|
+
separator: separator,
|
|
4635
|
+
column_naming: column_naming
|
|
3269
4636
|
)
|
|
4637
|
+
.collect(optimizations: QueryOptFlags._eager)
|
|
3270
4638
|
end
|
|
3271
4639
|
|
|
3272
4640
|
# Unpivot a DataFrame from wide to long format.
|
|
@@ -3280,7 +4648,8 @@ module Polars
|
|
|
3280
4648
|
#
|
|
3281
4649
|
# @param on [Object]
|
|
3282
4650
|
# Column(s) or selector(s) to use as values variables; if `on`
|
|
3283
|
-
# is empty
|
|
4651
|
+
# is empty no columns will be used. If set to `nil` (default)
|
|
4652
|
+
# all columns that are not in `index` will be used.
|
|
3284
4653
|
# @param index [Object]
|
|
3285
4654
|
# Column(s) or selector(s) to use as identifier variables.
|
|
3286
4655
|
# @param variable_name [Object]
|
|
@@ -3313,13 +4682,12 @@ module Polars
|
|
|
3313
4682
|
# # │ y ┆ c ┆ 4 │
|
|
3314
4683
|
# # │ z ┆ c ┆ 6 │
|
|
3315
4684
|
# # └─────┴──────────┴───────┘
|
|
3316
|
-
def unpivot(on, index: nil, variable_name: nil, value_name: nil)
|
|
3317
|
-
on = on.nil? ?
|
|
4685
|
+
def unpivot(on = nil, index: nil, variable_name: nil, value_name: nil)
|
|
4686
|
+
on = on.nil? ? nil : Utils._expand_selectors(self, on)
|
|
3318
4687
|
index = index.nil? ? [] : Utils._expand_selectors(self, index)
|
|
3319
4688
|
|
|
3320
4689
|
_from_rbdf(_df.unpivot(on, index, value_name, variable_name))
|
|
3321
4690
|
end
|
|
3322
|
-
alias_method :melt, :unpivot
|
|
3323
4691
|
|
|
3324
4692
|
# Unstack a long table to a wide form without doing an aggregation.
|
|
3325
4693
|
#
|
|
@@ -3424,7 +4792,7 @@ module Polars
|
|
|
3424
4792
|
|
|
3425
4793
|
if how == "horizontal"
|
|
3426
4794
|
df = (
|
|
3427
|
-
df.
|
|
4795
|
+
df.with_columns(
|
|
3428
4796
|
(Polars.arange(0, n_cols * n_rows, eager: true) % n_cols).alias(
|
|
3429
4797
|
"__sort_order"
|
|
3430
4798
|
)
|
|
@@ -3447,14 +4815,18 @@ module Polars
|
|
|
3447
4815
|
|
|
3448
4816
|
# Split into multiple DataFrames partitioned by groups.
|
|
3449
4817
|
#
|
|
3450
|
-
# @param
|
|
4818
|
+
# @param by [Object]
|
|
3451
4819
|
# Groups to partition by.
|
|
4820
|
+
# @param more_by [Array]
|
|
4821
|
+
# Additional names of columns to group by, specified as positional arguments.
|
|
3452
4822
|
# @param maintain_order [Boolean]
|
|
3453
4823
|
# Keep predictable output order. This is slower as it requires an extra sort
|
|
3454
4824
|
# operation.
|
|
4825
|
+
# @param include_key [Boolean]
|
|
4826
|
+
# Include the columns used to partition the DataFrame in the output.
|
|
3455
4827
|
# @param as_dict [Boolean]
|
|
3456
|
-
# If true, return the partitions in a
|
|
3457
|
-
# values instead of
|
|
4828
|
+
# If true, return the partitions in a hash keyed by the distinct group
|
|
4829
|
+
# values instead of an array.
|
|
3458
4830
|
#
|
|
3459
4831
|
# @return [Object]
|
|
3460
4832
|
#
|
|
@@ -3496,7 +4868,7 @@ module Polars
|
|
|
3496
4868
|
# @example
|
|
3497
4869
|
# df.partition_by("foo", maintain_order: true, as_dict: true)
|
|
3498
4870
|
# # =>
|
|
3499
|
-
# # {"A"=>shape: (2, 3)
|
|
4871
|
+
# # {["A"]=>shape: (2, 3)
|
|
3500
4872
|
# # ┌─────┬─────┬─────┐
|
|
3501
4873
|
# # │ foo ┆ N ┆ bar │
|
|
3502
4874
|
# # │ --- ┆ --- ┆ --- │
|
|
@@ -3504,7 +4876,7 @@ module Polars
|
|
|
3504
4876
|
# # ╞═════╪═════╪═════╡
|
|
3505
4877
|
# # │ A ┆ 1 ┆ k │
|
|
3506
4878
|
# # │ A ┆ 2 ┆ l │
|
|
3507
|
-
# # └─────┴─────┴─────┘, "B"=>shape: (2, 3)
|
|
4879
|
+
# # └─────┴─────┴─────┘, ["B"]=>shape: (2, 3)
|
|
3508
4880
|
# # ┌─────┬─────┬─────┐
|
|
3509
4881
|
# # │ foo ┆ N ┆ bar │
|
|
3510
4882
|
# # │ --- ┆ --- ┆ --- │
|
|
@@ -3512,7 +4884,7 @@ module Polars
|
|
|
3512
4884
|
# # ╞═════╪═════╪═════╡
|
|
3513
4885
|
# # │ B ┆ 2 ┆ m │
|
|
3514
4886
|
# # │ B ┆ 4 ┆ m │
|
|
3515
|
-
# # └─────┴─────┴─────┘, "C"=>shape: (1, 3)
|
|
4887
|
+
# # └─────┴─────┴─────┘, ["C"]=>shape: (1, 3)
|
|
3516
4888
|
# # ┌─────┬─────┬─────┐
|
|
3517
4889
|
# # │ foo ┆ N ┆ bar │
|
|
3518
4890
|
# # │ --- ┆ --- ┆ --- │
|
|
@@ -3520,30 +4892,26 @@ module Polars
|
|
|
3520
4892
|
# # ╞═════╪═════╪═════╡
|
|
3521
4893
|
# # │ C ┆ 2 ┆ l │
|
|
3522
4894
|
# # └─────┴─────┴─────┘}
|
|
3523
|
-
def partition_by(
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
groups = Array(groups)
|
|
3528
|
-
end
|
|
4895
|
+
def partition_by(by, *more_by, maintain_order: true, include_key: true, as_dict: false)
|
|
4896
|
+
by_parsed = Utils._expand_selectors(self, by, *more_by)
|
|
4897
|
+
|
|
4898
|
+
partitions = _df.partition_by(by_parsed, maintain_order, include_key).map { |df| _from_rbdf(df) }
|
|
3529
4899
|
|
|
3530
4900
|
if as_dict
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
_df.partition_by(groups, maintain_order, include_key).each do |df|
|
|
3534
|
-
df = _from_rbdf(df)
|
|
3535
|
-
out[df[groups][0, 0]] = df
|
|
3536
|
-
end
|
|
4901
|
+
if include_key
|
|
4902
|
+
names = partitions.map { |p| p.select(by_parsed).row(0) }
|
|
3537
4903
|
else
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
4904
|
+
if !maintain_order
|
|
4905
|
+
msg = "cannot use `partition_by` with `maintain_order: false, include_key: false, as_dict: true`"
|
|
4906
|
+
raise ArgumentError, msg
|
|
3541
4907
|
end
|
|
4908
|
+
names = select(by_parsed).unique(maintain_order: true).rows
|
|
3542
4909
|
end
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
_df.partition_by(groups, maintain_order, include_key).map { |df| _from_rbdf(df) }
|
|
4910
|
+
|
|
4911
|
+
return names.zip(partitions).to_h
|
|
3546
4912
|
end
|
|
4913
|
+
|
|
4914
|
+
partitions
|
|
3547
4915
|
end
|
|
3548
4916
|
|
|
3549
4917
|
# Shift values by the given period.
|
|
@@ -3589,41 +4957,8 @@ module Polars
|
|
|
3589
4957
|
# # │ 3 ┆ 8 ┆ c │
|
|
3590
4958
|
# # │ null ┆ null ┆ null │
|
|
3591
4959
|
# # └──────┴──────┴──────┘
|
|
3592
|
-
def shift(n, fill_value: nil)
|
|
3593
|
-
lazy.shift(n, fill_value: fill_value).collect(
|
|
3594
|
-
end
|
|
3595
|
-
|
|
3596
|
-
# Shift the values by a given period and fill the resulting null values.
|
|
3597
|
-
#
|
|
3598
|
-
# @param periods [Integer]
|
|
3599
|
-
# Number of places to shift (may be negative).
|
|
3600
|
-
# @param fill_value [Object]
|
|
3601
|
-
# fill nil values with this value.
|
|
3602
|
-
#
|
|
3603
|
-
# @return [DataFrame]
|
|
3604
|
-
#
|
|
3605
|
-
# @example
|
|
3606
|
-
# df = Polars::DataFrame.new(
|
|
3607
|
-
# {
|
|
3608
|
-
# "foo" => [1, 2, 3],
|
|
3609
|
-
# "bar" => [6, 7, 8],
|
|
3610
|
-
# "ham" => ["a", "b", "c"]
|
|
3611
|
-
# }
|
|
3612
|
-
# )
|
|
3613
|
-
# df.shift_and_fill(1, 0)
|
|
3614
|
-
# # =>
|
|
3615
|
-
# # shape: (3, 3)
|
|
3616
|
-
# # ┌─────┬─────┬─────┐
|
|
3617
|
-
# # │ foo ┆ bar ┆ ham │
|
|
3618
|
-
# # │ --- ┆ --- ┆ --- │
|
|
3619
|
-
# # │ i64 ┆ i64 ┆ str │
|
|
3620
|
-
# # ╞═════╪═════╪═════╡
|
|
3621
|
-
# # │ 0 ┆ 0 ┆ 0 │
|
|
3622
|
-
# # │ 1 ┆ 6 ┆ a │
|
|
3623
|
-
# # │ 2 ┆ 7 ┆ b │
|
|
3624
|
-
# # └─────┴─────┴─────┘
|
|
3625
|
-
def shift_and_fill(periods, fill_value)
|
|
3626
|
-
shift(periods, fill_value: fill_value)
|
|
4960
|
+
def shift(n = 1, fill_value: nil)
|
|
4961
|
+
lazy.shift(n, fill_value: fill_value).collect(optimizations: QueryOptFlags._eager)
|
|
3627
4962
|
end
|
|
3628
4963
|
|
|
3629
4964
|
# Get a mask of all duplicated rows in this DataFrame.
|
|
@@ -3679,6 +5014,16 @@ module Polars
|
|
|
3679
5014
|
# Start a lazy query from this point.
|
|
3680
5015
|
#
|
|
3681
5016
|
# @return [LazyFrame]
|
|
5017
|
+
#
|
|
5018
|
+
# @example
|
|
5019
|
+
# df = Polars::DataFrame.new(
|
|
5020
|
+
# {
|
|
5021
|
+
# "a" => [nil, 2, 3, 4],
|
|
5022
|
+
# "b" => [0.5, nil, 2.5, 13],
|
|
5023
|
+
# "c" => [true, true, false, nil]
|
|
5024
|
+
# }
|
|
5025
|
+
# )
|
|
5026
|
+
# df.lazy
|
|
3682
5027
|
def lazy
|
|
3683
5028
|
wrap_ldf(_df.lazy)
|
|
3684
5029
|
end
|
|
@@ -3772,7 +5117,27 @@ module Polars
|
|
|
3772
5117
|
# # │ 10 │
|
|
3773
5118
|
# # └─────────┘
|
|
3774
5119
|
def select(*exprs, **named_exprs)
|
|
3775
|
-
lazy.select(*exprs, **named_exprs).collect(
|
|
5120
|
+
lazy.select(*exprs, **named_exprs).collect(optimizations: QueryOptFlags._eager)
|
|
5121
|
+
end
|
|
5122
|
+
|
|
5123
|
+
# Select columns from this DataFrame.
|
|
5124
|
+
#
|
|
5125
|
+
# This will run all expression sequentially instead of in parallel.
|
|
5126
|
+
# Use this when the work per expression is cheap.
|
|
5127
|
+
#
|
|
5128
|
+
# @param exprs [Array]
|
|
5129
|
+
# Column(s) to select, specified as positional arguments.
|
|
5130
|
+
# Accepts expression input. Strings are parsed as column names,
|
|
5131
|
+
# other non-expression inputs are parsed as literals.
|
|
5132
|
+
# @param named_exprs [Hash]
|
|
5133
|
+
# Additional columns to select, specified as keyword arguments.
|
|
5134
|
+
# The columns will be renamed to the keyword used.
|
|
5135
|
+
#
|
|
5136
|
+
# @return [DataFrame]
|
|
5137
|
+
def select_seq(*exprs, **named_exprs)
|
|
5138
|
+
lazy
|
|
5139
|
+
.select_seq(*exprs, **named_exprs)
|
|
5140
|
+
.collect(optimizations: QueryOptFlags._eager)
|
|
3776
5141
|
end
|
|
3777
5142
|
|
|
3778
5143
|
# Add columns to this DataFrame.
|
|
@@ -3884,7 +5249,32 @@ module Polars
|
|
|
3884
5249
|
# # │ 4 ┆ 13.0 ┆ true ┆ 52.0 ┆ false │
|
|
3885
5250
|
# # └─────┴──────┴───────┴──────┴───────┘
|
|
3886
5251
|
def with_columns(*exprs, **named_exprs)
|
|
3887
|
-
lazy.with_columns(*exprs, **named_exprs).collect(
|
|
5252
|
+
lazy.with_columns(*exprs, **named_exprs).collect(optimizations: QueryOptFlags._eager)
|
|
5253
|
+
end
|
|
5254
|
+
|
|
5255
|
+
# Add columns to this DataFrame.
|
|
5256
|
+
#
|
|
5257
|
+
# Added columns will replace existing columns with the same name.
|
|
5258
|
+
#
|
|
5259
|
+
# This will run all expression sequentially instead of in parallel.
|
|
5260
|
+
# Use this when the work per expression is cheap.
|
|
5261
|
+
#
|
|
5262
|
+
# @param exprs [Array]
|
|
5263
|
+
# Column(s) to add, specified as positional arguments.
|
|
5264
|
+
# Accepts expression input. Strings are parsed as column names, other
|
|
5265
|
+
# non-expression inputs are parsed as literals.
|
|
5266
|
+
# @param named_exprs [Hash]
|
|
5267
|
+
# Additional columns to add, specified as keyword arguments.
|
|
5268
|
+
# The columns will be renamed to the keyword used.
|
|
5269
|
+
#
|
|
5270
|
+
# @return [DataFrame]
|
|
5271
|
+
def with_columns_seq(
|
|
5272
|
+
*exprs,
|
|
5273
|
+
**named_exprs
|
|
5274
|
+
)
|
|
5275
|
+
lazy
|
|
5276
|
+
.with_columns_seq(*exprs, **named_exprs)
|
|
5277
|
+
.collect(optimizations: QueryOptFlags._eager)
|
|
3888
5278
|
end
|
|
3889
5279
|
|
|
3890
5280
|
# Get number of chunks used by the ChunkedArrays of this DataFrame.
|
|
@@ -3939,14 +5329,32 @@ module Polars
|
|
|
3939
5329
|
# # ╞═════╪═════╪═════╡
|
|
3940
5330
|
# # │ 3 ┆ 8 ┆ c │
|
|
3941
5331
|
# # └─────┴─────┴─────┘
|
|
3942
|
-
def max
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
5332
|
+
def max
|
|
5333
|
+
lazy.max.collect(optimizations: QueryOptFlags._eager)
|
|
5334
|
+
end
|
|
5335
|
+
|
|
5336
|
+
# Get the maximum value horizontally across columns.
|
|
5337
|
+
#
|
|
5338
|
+
# @return [Series]
|
|
5339
|
+
#
|
|
5340
|
+
# @example
|
|
5341
|
+
# df = Polars::DataFrame.new(
|
|
5342
|
+
# {
|
|
5343
|
+
# "foo" => [1, 2, 3],
|
|
5344
|
+
# "bar" => [4.0, 5.0, 6.0]
|
|
5345
|
+
# }
|
|
5346
|
+
# )
|
|
5347
|
+
# df.max_horizontal
|
|
5348
|
+
# # =>
|
|
5349
|
+
# # shape: (3,)
|
|
5350
|
+
# # Series: 'max' [f64]
|
|
5351
|
+
# # [
|
|
5352
|
+
# # 4.0
|
|
5353
|
+
# # 5.0
|
|
5354
|
+
# # 6.0
|
|
5355
|
+
# # ]
|
|
5356
|
+
def max_horizontal
|
|
5357
|
+
select(max: F.max_horizontal(F.all)).to_series
|
|
3950
5358
|
end
|
|
3951
5359
|
|
|
3952
5360
|
# Aggregate the columns of this DataFrame to their minimum value.
|
|
@@ -3971,22 +5379,35 @@ module Polars
|
|
|
3971
5379
|
# # ╞═════╪═════╪═════╡
|
|
3972
5380
|
# # │ 1 ┆ 6 ┆ a │
|
|
3973
5381
|
# # └─────┴─────┴─────┘
|
|
3974
|
-
def min
|
|
3975
|
-
|
|
3976
|
-
lazy.min.collect(_eager: true)
|
|
3977
|
-
elsif axis == 1
|
|
3978
|
-
Utils.wrap_s(_df.min_horizontal)
|
|
3979
|
-
else
|
|
3980
|
-
raise ArgumentError, "Axis should be 0 or 1."
|
|
3981
|
-
end
|
|
5382
|
+
def min
|
|
5383
|
+
lazy.min.collect(optimizations: QueryOptFlags._eager)
|
|
3982
5384
|
end
|
|
3983
5385
|
|
|
3984
|
-
#
|
|
5386
|
+
# Get the minimum value horizontally across columns.
|
|
3985
5387
|
#
|
|
3986
|
-
# @
|
|
3987
|
-
#
|
|
3988
|
-
# @
|
|
3989
|
-
#
|
|
5388
|
+
# @return [Series]
|
|
5389
|
+
#
|
|
5390
|
+
# @example
|
|
5391
|
+
# df = Polars::DataFrame.new(
|
|
5392
|
+
# {
|
|
5393
|
+
# "foo" => [1, 2, 3],
|
|
5394
|
+
# "bar" => [4.0, 5.0, 6.0]
|
|
5395
|
+
# }
|
|
5396
|
+
# )
|
|
5397
|
+
# df.min_horizontal
|
|
5398
|
+
# # =>
|
|
5399
|
+
# # shape: (3,)
|
|
5400
|
+
# # Series: 'min' [f64]
|
|
5401
|
+
# # [
|
|
5402
|
+
# # 1.0
|
|
5403
|
+
# # 2.0
|
|
5404
|
+
# # 3.0
|
|
5405
|
+
# # ]
|
|
5406
|
+
def min_horizontal
|
|
5407
|
+
select(min: F.min_horizontal(F.all)).to_series
|
|
5408
|
+
end
|
|
5409
|
+
|
|
5410
|
+
# Aggregate the columns of this DataFrame to their sum value.
|
|
3990
5411
|
#
|
|
3991
5412
|
# @return [DataFrame]
|
|
3992
5413
|
#
|
|
@@ -4008,35 +5429,42 @@ module Polars
|
|
|
4008
5429
|
# # ╞═════╪═════╪══════╡
|
|
4009
5430
|
# # │ 6 ┆ 21 ┆ null │
|
|
4010
5431
|
# # └─────┴─────┴──────┘
|
|
5432
|
+
def sum
|
|
5433
|
+
lazy.sum.collect(optimizations: QueryOptFlags._eager)
|
|
5434
|
+
end
|
|
5435
|
+
|
|
5436
|
+
# Sum all values horizontally across columns.
|
|
5437
|
+
#
|
|
5438
|
+
# @param ignore_nulls [Boolean]
|
|
5439
|
+
# Ignore null values (default).
|
|
5440
|
+
# If set to `false`, any null value in the input will lead to a null output.
|
|
5441
|
+
#
|
|
5442
|
+
# @return [Series]
|
|
4011
5443
|
#
|
|
4012
5444
|
# @example
|
|
4013
|
-
# df.
|
|
5445
|
+
# df = Polars::DataFrame.new(
|
|
5446
|
+
# {
|
|
5447
|
+
# "foo" => [1, 2, 3],
|
|
5448
|
+
# "bar" => [4.0, 5.0, 6.0]
|
|
5449
|
+
# }
|
|
5450
|
+
# )
|
|
5451
|
+
# df.sum_horizontal
|
|
4014
5452
|
# # =>
|
|
4015
5453
|
# # shape: (3,)
|
|
4016
|
-
# # Series: '
|
|
5454
|
+
# # Series: 'sum' [f64]
|
|
4017
5455
|
# # [
|
|
4018
|
-
# #
|
|
4019
|
-
# #
|
|
4020
|
-
# #
|
|
5456
|
+
# # 5.0
|
|
5457
|
+
# # 7.0
|
|
5458
|
+
# # 9.0
|
|
4021
5459
|
# # ]
|
|
4022
|
-
def
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
when 1
|
|
4027
|
-
Utils.wrap_s(_df.sum_horizontal(null_strategy))
|
|
4028
|
-
else
|
|
4029
|
-
raise ArgumentError, "Axis should be 0 or 1."
|
|
4030
|
-
end
|
|
5460
|
+
def sum_horizontal(ignore_nulls: true)
|
|
5461
|
+
select(
|
|
5462
|
+
sum: F.sum_horizontal(F.all, ignore_nulls: ignore_nulls)
|
|
5463
|
+
).to_series
|
|
4031
5464
|
end
|
|
4032
5465
|
|
|
4033
5466
|
# Aggregate the columns of this DataFrame to their mean value.
|
|
4034
5467
|
#
|
|
4035
|
-
# @param axis [Integer]
|
|
4036
|
-
# Either 0 or 1.
|
|
4037
|
-
# @param null_strategy ["ignore", "propagate"]
|
|
4038
|
-
# This argument is only used if axis == 1.
|
|
4039
|
-
#
|
|
4040
5468
|
# @return [DataFrame]
|
|
4041
5469
|
#
|
|
4042
5470
|
# @example
|
|
@@ -4057,40 +5485,63 @@ module Polars
|
|
|
4057
5485
|
# # ╞═════╪═════╪══════╡
|
|
4058
5486
|
# # │ 2.0 ┆ 7.0 ┆ null │
|
|
4059
5487
|
# # └─────┴─────┴──────┘
|
|
4060
|
-
def mean
|
|
4061
|
-
|
|
4062
|
-
when 0
|
|
4063
|
-
lazy.mean.collect(_eager: true)
|
|
4064
|
-
when 1
|
|
4065
|
-
Utils.wrap_s(_df.mean_horizontal(null_strategy))
|
|
4066
|
-
else
|
|
4067
|
-
raise ArgumentError, "Axis should be 0 or 1."
|
|
4068
|
-
end
|
|
5488
|
+
def mean
|
|
5489
|
+
lazy.mean.collect(optimizations: QueryOptFlags._eager)
|
|
4069
5490
|
end
|
|
4070
5491
|
|
|
4071
|
-
#
|
|
5492
|
+
# Take the mean of all values horizontally across columns.
|
|
4072
5493
|
#
|
|
4073
|
-
# @param
|
|
4074
|
-
#
|
|
5494
|
+
# @param ignore_nulls [Boolean]
|
|
5495
|
+
# Ignore null values (default).
|
|
5496
|
+
# If set to `false`, any null value in the input will lead to a null output.
|
|
4075
5497
|
#
|
|
4076
|
-
# @return [
|
|
5498
|
+
# @return [Series]
|
|
4077
5499
|
#
|
|
4078
5500
|
# @example
|
|
4079
5501
|
# df = Polars::DataFrame.new(
|
|
4080
5502
|
# {
|
|
4081
5503
|
# "foo" => [1, 2, 3],
|
|
4082
|
-
# "bar" => [
|
|
4083
|
-
# "ham" => ["a", "b", "c"]
|
|
5504
|
+
# "bar" => [4.0, 5.0, 6.0]
|
|
4084
5505
|
# }
|
|
4085
5506
|
# )
|
|
4086
|
-
# df.
|
|
5507
|
+
# df.mean_horizontal
|
|
4087
5508
|
# # =>
|
|
4088
|
-
# # shape: (
|
|
4089
|
-
# #
|
|
4090
|
-
# #
|
|
4091
|
-
# #
|
|
4092
|
-
# #
|
|
4093
|
-
# #
|
|
5509
|
+
# # shape: (3,)
|
|
5510
|
+
# # Series: 'mean' [f64]
|
|
5511
|
+
# # [
|
|
5512
|
+
# # 2.5
|
|
5513
|
+
# # 3.5
|
|
5514
|
+
# # 4.5
|
|
5515
|
+
# # ]
|
|
5516
|
+
def mean_horizontal(ignore_nulls: true)
|
|
5517
|
+
select(
|
|
5518
|
+
mean: F.mean_horizontal(F.all, ignore_nulls: ignore_nulls)
|
|
5519
|
+
).to_series
|
|
5520
|
+
end
|
|
5521
|
+
|
|
5522
|
+
# Aggregate the columns of this DataFrame to their standard deviation value.
|
|
5523
|
+
#
|
|
5524
|
+
# @param ddof [Integer]
|
|
5525
|
+
# Degrees of freedom
|
|
5526
|
+
#
|
|
5527
|
+
# @return [DataFrame]
|
|
5528
|
+
#
|
|
5529
|
+
# @example
|
|
5530
|
+
# df = Polars::DataFrame.new(
|
|
5531
|
+
# {
|
|
5532
|
+
# "foo" => [1, 2, 3],
|
|
5533
|
+
# "bar" => [6, 7, 8],
|
|
5534
|
+
# "ham" => ["a", "b", "c"]
|
|
5535
|
+
# }
|
|
5536
|
+
# )
|
|
5537
|
+
# df.std
|
|
5538
|
+
# # =>
|
|
5539
|
+
# # shape: (1, 3)
|
|
5540
|
+
# # ┌─────┬─────┬──────┐
|
|
5541
|
+
# # │ foo ┆ bar ┆ ham │
|
|
5542
|
+
# # │ --- ┆ --- ┆ --- │
|
|
5543
|
+
# # │ f64 ┆ f64 ┆ str │
|
|
5544
|
+
# # ╞═════╪═════╪══════╡
|
|
4094
5545
|
# # │ 1.0 ┆ 1.0 ┆ null │
|
|
4095
5546
|
# # └─────┴─────┴──────┘
|
|
4096
5547
|
#
|
|
@@ -4106,7 +5557,7 @@ module Polars
|
|
|
4106
5557
|
# # │ 0.816497 ┆ 0.816497 ┆ null │
|
|
4107
5558
|
# # └──────────┴──────────┴──────┘
|
|
4108
5559
|
def std(ddof: 1)
|
|
4109
|
-
lazy.std(ddof: ddof).collect(
|
|
5560
|
+
lazy.std(ddof: ddof).collect(optimizations: QueryOptFlags._eager)
|
|
4110
5561
|
end
|
|
4111
5562
|
|
|
4112
5563
|
# Aggregate the columns of this DataFrame to their variance value.
|
|
@@ -4147,7 +5598,7 @@ module Polars
|
|
|
4147
5598
|
# # │ 0.666667 ┆ 0.666667 ┆ null │
|
|
4148
5599
|
# # └──────────┴──────────┴──────┘
|
|
4149
5600
|
def var(ddof: 1)
|
|
4150
|
-
lazy.var(ddof: ddof).collect(
|
|
5601
|
+
lazy.var(ddof: ddof).collect(optimizations: QueryOptFlags._eager)
|
|
4151
5602
|
end
|
|
4152
5603
|
|
|
4153
5604
|
# Aggregate the columns of this DataFrame to their median value.
|
|
@@ -4173,7 +5624,7 @@ module Polars
|
|
|
4173
5624
|
# # │ 2.0 ┆ 7.0 ┆ null │
|
|
4174
5625
|
# # └─────┴─────┴──────┘
|
|
4175
5626
|
def median
|
|
4176
|
-
lazy.median.collect(
|
|
5627
|
+
lazy.median.collect(optimizations: QueryOptFlags._eager)
|
|
4177
5628
|
end
|
|
4178
5629
|
|
|
4179
5630
|
# Aggregate the columns of this DataFrame to their product values.
|
|
@@ -4230,14 +5681,20 @@ module Polars
|
|
|
4230
5681
|
# # │ 2.0 ┆ 7.0 ┆ null │
|
|
4231
5682
|
# # └─────┴─────┴──────┘
|
|
4232
5683
|
def quantile(quantile, interpolation: "nearest")
|
|
4233
|
-
lazy.quantile(quantile, interpolation: interpolation).collect(
|
|
5684
|
+
lazy.quantile(quantile, interpolation: interpolation).collect(optimizations: QueryOptFlags._eager)
|
|
4234
5685
|
end
|
|
4235
5686
|
|
|
4236
5687
|
# Get one hot encoded dummy variables.
|
|
4237
5688
|
#
|
|
4238
|
-
# @param columns
|
|
5689
|
+
# @param columns [Array]
|
|
4239
5690
|
# A subset of columns to convert to dummy variables. `nil` means
|
|
4240
5691
|
# "all columns".
|
|
5692
|
+
# @param separator [String]
|
|
5693
|
+
# Separator/delimiter used when generating column names.
|
|
5694
|
+
# @param drop_first [Boolean]
|
|
5695
|
+
# Remove the first category from the variables being encoded.
|
|
5696
|
+
# @param drop_nulls [Boolean]
|
|
5697
|
+
# If there are `nil` values in the series, a `null` column is not generated
|
|
4241
5698
|
#
|
|
4242
5699
|
# @return [DataFrame]
|
|
4243
5700
|
#
|
|
@@ -4260,11 +5717,11 @@ module Polars
|
|
|
4260
5717
|
# # │ 1 ┆ 0 ┆ 1 ┆ 0 ┆ 1 ┆ 0 │
|
|
4261
5718
|
# # │ 0 ┆ 1 ┆ 0 ┆ 1 ┆ 0 ┆ 1 │
|
|
4262
5719
|
# # └───────┴───────┴───────┴───────┴───────┴───────┘
|
|
4263
|
-
def to_dummies(columns: nil, separator: "_", drop_first: false)
|
|
5720
|
+
def to_dummies(columns: nil, separator: "_", drop_first: false, drop_nulls: false)
|
|
4264
5721
|
if columns.is_a?(::String)
|
|
4265
5722
|
columns = [columns]
|
|
4266
5723
|
end
|
|
4267
|
-
_from_rbdf(_df.to_dummies(columns, separator, drop_first))
|
|
5724
|
+
_from_rbdf(_df.to_dummies(columns, separator, drop_first, drop_nulls))
|
|
4268
5725
|
end
|
|
4269
5726
|
|
|
4270
5727
|
# Drop duplicate rows from this DataFrame.
|
|
@@ -4291,7 +5748,7 @@ module Polars
|
|
|
4291
5748
|
# "c" => [true, true, true, false, true, true]
|
|
4292
5749
|
# }
|
|
4293
5750
|
# )
|
|
4294
|
-
# df.unique
|
|
5751
|
+
# df.unique(maintain_order: true)
|
|
4295
5752
|
# # =>
|
|
4296
5753
|
# # shape: (5, 3)
|
|
4297
5754
|
# # ┌─────┬─────┬───────┐
|
|
@@ -4305,11 +5762,11 @@ module Polars
|
|
|
4305
5762
|
# # │ 4 ┆ 3.0 ┆ true │
|
|
4306
5763
|
# # │ 5 ┆ 3.0 ┆ true │
|
|
4307
5764
|
# # └─────┴─────┴───────┘
|
|
4308
|
-
def unique(maintain_order:
|
|
5765
|
+
def unique(maintain_order: false, subset: nil, keep: "any")
|
|
4309
5766
|
self._from_rbdf(
|
|
4310
5767
|
lazy
|
|
4311
5768
|
.unique(maintain_order: maintain_order, subset: subset, keep: keep)
|
|
4312
|
-
.collect(
|
|
5769
|
+
.collect(optimizations: QueryOptFlags._eager)
|
|
4313
5770
|
._df
|
|
4314
5771
|
)
|
|
4315
5772
|
end
|
|
@@ -4402,9 +5859,9 @@ module Polars
|
|
|
4402
5859
|
# Sample from this DataFrame.
|
|
4403
5860
|
#
|
|
4404
5861
|
# @param n [Integer]
|
|
4405
|
-
# Number of items to return. Cannot be used with `
|
|
4406
|
-
# `
|
|
4407
|
-
# @param
|
|
5862
|
+
# Number of items to return. Cannot be used with `fraction`. Defaults to 1 if
|
|
5863
|
+
# `fraction` is nil.
|
|
5864
|
+
# @param fraction [Float]
|
|
4408
5865
|
# Fraction of items to return. Cannot be used with `n`.
|
|
4409
5866
|
# @param with_replacement [Boolean]
|
|
4410
5867
|
# Allow values to be sampled more than once.
|
|
@@ -4432,25 +5889,25 @@ module Polars
|
|
|
4432
5889
|
# # │ --- ┆ --- ┆ --- │
|
|
4433
5890
|
# # │ i64 ┆ i64 ┆ str │
|
|
4434
5891
|
# # ╞═════╪═════╪═════╡
|
|
4435
|
-
# # │
|
|
5892
|
+
# # │ 1 ┆ 6 ┆ a │
|
|
4436
5893
|
# # │ 2 ┆ 7 ┆ b │
|
|
4437
5894
|
# # └─────┴─────┴─────┘
|
|
4438
5895
|
def sample(
|
|
4439
5896
|
n: nil,
|
|
4440
|
-
|
|
5897
|
+
fraction: nil,
|
|
4441
5898
|
with_replacement: false,
|
|
4442
5899
|
shuffle: false,
|
|
4443
5900
|
seed: nil
|
|
4444
5901
|
)
|
|
4445
|
-
if !n.nil? && !
|
|
4446
|
-
raise ArgumentError, "cannot specify both `n` and `
|
|
5902
|
+
if !n.nil? && !fraction.nil?
|
|
5903
|
+
raise ArgumentError, "cannot specify both `n` and `fraction`"
|
|
4447
5904
|
end
|
|
4448
5905
|
|
|
4449
|
-
if n.nil? && !
|
|
4450
|
-
|
|
5906
|
+
if n.nil? && !fraction.nil?
|
|
5907
|
+
fraction = Series.new("fraction", [fraction]) unless fraction.is_a?(Series)
|
|
4451
5908
|
|
|
4452
5909
|
return _from_rbdf(
|
|
4453
|
-
_df.sample_frac(
|
|
5910
|
+
_df.sample_frac(fraction._s, with_replacement, shuffle, seed)
|
|
4454
5911
|
)
|
|
4455
5912
|
end
|
|
4456
5913
|
|
|
@@ -4658,6 +6115,85 @@ module Polars
|
|
|
4658
6115
|
end
|
|
4659
6116
|
end
|
|
4660
6117
|
|
|
6118
|
+
# Convert columnar data to rows as Ruby arrays in a hash keyed by some column.
|
|
6119
|
+
#
|
|
6120
|
+
# This method is like `rows`, but instead of returning rows in a flat list, rows
|
|
6121
|
+
# are grouped by the values in the `key` column(s) and returned as a hash.
|
|
6122
|
+
#
|
|
6123
|
+
# Note that this method should not be used in place of native operations, due to
|
|
6124
|
+
# the high cost of materializing all frame data out into a hash; it should
|
|
6125
|
+
# be used only when you need to move the values out into a Ruby data structure
|
|
6126
|
+
# or other object that cannot operate directly with Polars/Arrow.
|
|
6127
|
+
#
|
|
6128
|
+
# @param key [Object]
|
|
6129
|
+
# The column(s) to use as the key for the returned hash. If multiple
|
|
6130
|
+
# columns are specified, the key will be a tuple of those values, otherwise
|
|
6131
|
+
# it will be a string.
|
|
6132
|
+
# @param named [Boolean]
|
|
6133
|
+
# Return hashes instead of arrays. The hashes are a mapping of
|
|
6134
|
+
# column name to row value. This is more expensive than returning an
|
|
6135
|
+
# array, but allows for accessing values by column name.
|
|
6136
|
+
# @param include_key [Boolean]
|
|
6137
|
+
# Include key values inline with the associated data (by default the key
|
|
6138
|
+
# values are omitted as a memory/performance optimisation, as they can be
|
|
6139
|
+
# reoconstructed from the key).
|
|
6140
|
+
# @param unique [Boolean]
|
|
6141
|
+
# Indicate that the key is unique; this will result in a 1:1 mapping from
|
|
6142
|
+
# key to a single associated row. Note that if the key is *not* actually
|
|
6143
|
+
# unique the last row with the given key will be returned.
|
|
6144
|
+
#
|
|
6145
|
+
# @return [Hash]
|
|
6146
|
+
#
|
|
6147
|
+
# @example Group rows by the given key column(s):
|
|
6148
|
+
# df = Polars::DataFrame.new(
|
|
6149
|
+
# {
|
|
6150
|
+
# "w" => ["a", "b", "b", "a"],
|
|
6151
|
+
# "x" => ["q", "q", "q", "k"],
|
|
6152
|
+
# "y" => [1.0, 2.5, 3.0, 4.5],
|
|
6153
|
+
# "z" => [9, 8, 7, 6]
|
|
6154
|
+
# }
|
|
6155
|
+
# )
|
|
6156
|
+
# df.rows_by_key(["w"])
|
|
6157
|
+
# # => {"a"=>[["q", 1.0, 9], ["k", 4.5, 6]], "b"=>[["q", 2.5, 8], ["q", 3.0, 7]]}
|
|
6158
|
+
#
|
|
6159
|
+
# @example Return the same row groupings as hashes:
|
|
6160
|
+
# df.rows_by_key(["w"], named: true)
|
|
6161
|
+
# # => {"a"=>[{"x"=>"q", "y"=>1.0, "z"=>9}, {"x"=>"k", "y"=>4.5, "z"=>6}], "b"=>[{"x"=>"q", "y"=>2.5, "z"=>8}, {"x"=>"q", "y"=>3.0, "z"=>7}]}
|
|
6162
|
+
#
|
|
6163
|
+
# @example Return row groupings, assuming keys are unique:
|
|
6164
|
+
# df.rows_by_key(["z"], unique: true)
|
|
6165
|
+
# # => {9=>["a", "q", 1.0], 8=>["b", "q", 2.5], 7=>["b", "q", 3.0], 6=>["a", "k", 4.5]}
|
|
6166
|
+
#
|
|
6167
|
+
# @example Return row groupings as hashes, assuming keys are unique:
|
|
6168
|
+
# df.rows_by_key(["z"], named: true, unique: true)
|
|
6169
|
+
# # => {9=>{"w"=>"a", "x"=>"q", "y"=>1.0}, 8=>{"w"=>"b", "x"=>"q", "y"=>2.5}, 7=>{"w"=>"b", "x"=>"q", "y"=>3.0}, 6=>{"w"=>"a", "x"=>"k", "y"=>4.5}}
|
|
6170
|
+
#
|
|
6171
|
+
# @example Return hash rows grouped by a compound key, including key values:
|
|
6172
|
+
# df.rows_by_key(["w", "x"], named: true, include_key: true)
|
|
6173
|
+
# # => {["a", "q"]=>[{"w"=>"a", "x"=>"q", "y"=>1.0, "z"=>9}], ["b", "q"]=>[{"w"=>"b", "x"=>"q", "y"=>2.5, "z"=>8}, {"w"=>"b", "x"=>"q", "y"=>3.0, "z"=>7}], ["a", "k"]=>[{"w"=>"a", "x"=>"k", "y"=>4.5, "z"=>6}]}
|
|
6174
|
+
def rows_by_key(key, named: false, include_key: false, unique: false)
|
|
6175
|
+
key = Utils._expand_selectors(self, key)
|
|
6176
|
+
|
|
6177
|
+
keys = key.size == 1 ? get_column(key[0]) : select(key).iter_rows
|
|
6178
|
+
|
|
6179
|
+
if include_key
|
|
6180
|
+
values = self
|
|
6181
|
+
else
|
|
6182
|
+
data_cols = schema.names - key
|
|
6183
|
+
values = select(data_cols)
|
|
6184
|
+
end
|
|
6185
|
+
|
|
6186
|
+
zipped = keys.each.zip(values.iter_rows(named: named))
|
|
6187
|
+
|
|
6188
|
+
# if unique, we expect to write just one entry per key; otherwise, we're
|
|
6189
|
+
# returning a list of rows for each key, so append into a hash of arrays.
|
|
6190
|
+
if unique
|
|
6191
|
+
zipped.to_h
|
|
6192
|
+
else
|
|
6193
|
+
zipped.each_with_object({}) { |(key, data), h| (h[key] ||= []) << data }
|
|
6194
|
+
end
|
|
6195
|
+
end
|
|
6196
|
+
|
|
4661
6197
|
# Returns an iterator over the DataFrame of rows of Ruby-native values.
|
|
4662
6198
|
#
|
|
4663
6199
|
# @param named [Boolean]
|
|
@@ -4686,7 +6222,7 @@ module Polars
|
|
|
4686
6222
|
# @example
|
|
4687
6223
|
# df.iter_rows(named: true).map { |row| row["b"] }
|
|
4688
6224
|
# # => [2, 4, 6]
|
|
4689
|
-
def iter_rows(named: false, buffer_size:
|
|
6225
|
+
def iter_rows(named: false, buffer_size: 512, &block)
|
|
4690
6226
|
return to_enum(:iter_rows, named: named, buffer_size: buffer_size) unless block_given?
|
|
4691
6227
|
|
|
4692
6228
|
# load into the local namespace for a modest performance boost in the hot loops
|
|
@@ -4737,6 +6273,90 @@ module Polars
|
|
|
4737
6273
|
iter_rows(named: named, buffer_size: buffer_size, &block)
|
|
4738
6274
|
end
|
|
4739
6275
|
|
|
6276
|
+
# Returns an iterator over the columns of this DataFrame.
|
|
6277
|
+
#
|
|
6278
|
+
# @return [Object]
|
|
6279
|
+
#
|
|
6280
|
+
# @note
|
|
6281
|
+
# Consider whether you can use `all` instead.
|
|
6282
|
+
# If you can, it will be more efficient.
|
|
6283
|
+
#
|
|
6284
|
+
# @example
|
|
6285
|
+
# df = Polars::DataFrame.new(
|
|
6286
|
+
# {
|
|
6287
|
+
# "a" => [1, 3, 5],
|
|
6288
|
+
# "b" => [2, 4, 6]
|
|
6289
|
+
# }
|
|
6290
|
+
# )
|
|
6291
|
+
# df.iter_columns.map { |s| s.name }
|
|
6292
|
+
# # => ["a", "b"]
|
|
6293
|
+
#
|
|
6294
|
+
# @example If you're using this to modify a dataframe's columns, e.g.
|
|
6295
|
+
# # Do NOT do this
|
|
6296
|
+
# Polars::DataFrame.new(df.iter_columns.map { |column| column * 2 })
|
|
6297
|
+
# # =>
|
|
6298
|
+
# # shape: (3, 2)
|
|
6299
|
+
# # ┌─────┬─────┐
|
|
6300
|
+
# # │ a ┆ b │
|
|
6301
|
+
# # │ --- ┆ --- │
|
|
6302
|
+
# # │ i64 ┆ i64 │
|
|
6303
|
+
# # ╞═════╪═════╡
|
|
6304
|
+
# # │ 2 ┆ 4 │
|
|
6305
|
+
# # │ 6 ┆ 8 │
|
|
6306
|
+
# # │ 10 ┆ 12 │
|
|
6307
|
+
# # └─────┴─────┘
|
|
6308
|
+
#
|
|
6309
|
+
# @example then consider whether you can use `all` instead:
|
|
6310
|
+
# df.select(Polars.all * 2)
|
|
6311
|
+
# # =>
|
|
6312
|
+
# # shape: (3, 2)
|
|
6313
|
+
# # ┌─────┬─────┐
|
|
6314
|
+
# # │ a ┆ b │
|
|
6315
|
+
# # │ --- ┆ --- │
|
|
6316
|
+
# # │ i64 ┆ i64 │
|
|
6317
|
+
# # ╞═════╪═════╡
|
|
6318
|
+
# # │ 2 ┆ 4 │
|
|
6319
|
+
# # │ 6 ┆ 8 │
|
|
6320
|
+
# # │ 10 ┆ 12 │
|
|
6321
|
+
# # └─────┴─────┘
|
|
6322
|
+
def iter_columns
|
|
6323
|
+
return to_enum(:iter_columns) unless block_given?
|
|
6324
|
+
|
|
6325
|
+
_df.get_columns.each do |s|
|
|
6326
|
+
yield Utils.wrap_s(s)
|
|
6327
|
+
end
|
|
6328
|
+
end
|
|
6329
|
+
|
|
6330
|
+
# Returns a non-copying iterator of slices over the underlying DataFrame.
|
|
6331
|
+
#
|
|
6332
|
+
# @param n_rows [Integer]
|
|
6333
|
+
# Determines the number of rows contained in each DataFrame slice.
|
|
6334
|
+
#
|
|
6335
|
+
# @return [Object]
|
|
6336
|
+
#
|
|
6337
|
+
# @example
|
|
6338
|
+
# df = Polars::DataFrame.new(
|
|
6339
|
+
# {
|
|
6340
|
+
# "a" => 0...17_500,
|
|
6341
|
+
# "b" => Date.new(2023, 1, 1),
|
|
6342
|
+
# "c" => "klmnoopqrstuvwxyz"
|
|
6343
|
+
# },
|
|
6344
|
+
# schema_overrides: {"a" => Polars::Int32}
|
|
6345
|
+
# )
|
|
6346
|
+
# df.iter_slices.map.with_index do |frame, idx|
|
|
6347
|
+
# "#{frame.class.name}:[#{idx}]:#{frame.length}"
|
|
6348
|
+
# end
|
|
6349
|
+
# # => ["Polars::DataFrame:[0]:10000", "Polars::DataFrame:[1]:7500"]
|
|
6350
|
+
def iter_slices(n_rows: 10_000)
|
|
6351
|
+
return to_enum(:iter_slices, n_rows: n_rows) unless block_given?
|
|
6352
|
+
|
|
6353
|
+
offset = 0
|
|
6354
|
+
while offset < height
|
|
6355
|
+
yield slice(offset, n_rows)
|
|
6356
|
+
offset += n_rows
|
|
6357
|
+
end
|
|
6358
|
+
end
|
|
6359
|
+
|
|
4740
6360
|
# Shrink DataFrame memory usage.
|
|
4741
6361
|
#
|
|
4742
6362
|
# Shrinks to fit the exact capacity needed to hold the data.
|
|
@@ -4773,11 +6393,10 @@ module Polars
|
|
|
4773
6393
|
def gather_every(n, offset = 0)
|
|
4774
6394
|
select(F.col("*").gather_every(n, offset))
|
|
4775
6395
|
end
|
|
4776
|
-
alias_method :take_every, :gather_every
|
|
4777
6396
|
|
|
4778
6397
|
# Hash and combine the rows in this DataFrame.
|
|
4779
6398
|
#
|
|
4780
|
-
# The hash value is of type
|
|
6399
|
+
# The hash value is of type `UInt64`.
|
|
4781
6400
|
#
|
|
4782
6401
|
# @param seed [Integer]
|
|
4783
6402
|
# Random seed parameter. Defaults to 0.
|
|
@@ -4884,8 +6503,8 @@ module Polars
|
|
|
4884
6503
|
# # {4,"four"}
|
|
4885
6504
|
# # {5,"five"}
|
|
4886
6505
|
# # ]
|
|
4887
|
-
def to_struct(name)
|
|
4888
|
-
Utils.wrap_s(_df.to_struct(name))
|
|
6506
|
+
def to_struct(name = "")
|
|
6507
|
+
Utils.wrap_s(_df.to_struct(name, []))
|
|
4889
6508
|
end
|
|
4890
6509
|
|
|
4891
6510
|
# Decompose a struct into its fields.
|
|
@@ -4893,8 +6512,13 @@ module Polars
|
|
|
4893
6512
|
# The fields will be inserted into the `DataFrame` on the location of the
|
|
4894
6513
|
# `struct` type.
|
|
4895
6514
|
#
|
|
4896
|
-
# @param
|
|
4897
|
-
#
|
|
6515
|
+
# @param columns [Object]
|
|
6516
|
+
# Name of the struct column(s) that should be unnested.
|
|
6517
|
+
# @param more_columns [Array]
|
|
6518
|
+
# Additional columns to unnest, specified as positional arguments.
|
|
6519
|
+
# @param separator [String]
|
|
6520
|
+
# Rename output column names as combination of the struct column name,
|
|
6521
|
+
# name separator and field name.
|
|
4898
6522
|
#
|
|
4899
6523
|
# @return [DataFrame]
|
|
4900
6524
|
#
|
|
@@ -4920,17 +6544,10 @@ module Polars
|
|
|
4920
6544
|
# # │ foo ┆ 1 ┆ a ┆ true ┆ [1, 2] ┆ baz │
|
|
4921
6545
|
# # │ bar ┆ 2 ┆ b ┆ null ┆ [3] ┆ womp │
|
|
4922
6546
|
# # └────────┴─────┴─────┴──────┴───────────┴───────┘
|
|
4923
|
-
def unnest(
|
|
4924
|
-
|
|
4925
|
-
names = [names]
|
|
4926
|
-
end
|
|
4927
|
-
_from_rbdf(_df.unnest(names))
|
|
6547
|
+
def unnest(columns = nil, *more_columns, separator: nil)
|
|
6548
|
+
lazy.unnest(columns, *more_columns, separator: separator).collect(optimizations: QueryOptFlags._eager)
|
|
4928
6549
|
end
|
|
4929
6550
|
|
|
4930
|
-
# Requires NumPy
|
|
4931
|
-
# def corr
|
|
4932
|
-
# end
|
|
4933
|
-
|
|
4934
6551
|
# Take two sorted DataFrames and merge them by the sorted key.
|
|
4935
6552
|
#
|
|
4936
6553
|
# The output of this operation will also be sorted.
|
|
@@ -4943,6 +6560,10 @@ module Polars
|
|
|
4943
6560
|
# Other DataFrame that must be merged
|
|
4944
6561
|
# @param key [String]
|
|
4945
6562
|
# Key that is sorted.
|
|
6563
|
+
# @param maintain_order [Boolean]
|
|
6564
|
+
# If `true`, the output is guaranteed to have left-biased ordering
|
|
6565
|
+
# for equal keys: rows from the left frame appear before rows from
|
|
6566
|
+
# the right frame when their keys are equal.
|
|
4946
6567
|
#
|
|
4947
6568
|
# @return [DataFrame]
|
|
4948
6569
|
#
|
|
@@ -4969,30 +6590,235 @@ module Polars
|
|
|
4969
6590
|
# # │ steve ┆ 42 │
|
|
4970
6591
|
# # │ elise ┆ 44 │
|
|
4971
6592
|
# # └────────┴─────┘
|
|
4972
|
-
def merge_sorted(other, key)
|
|
4973
|
-
lazy.merge_sorted(other.lazy, key).collect(
|
|
6593
|
+
def merge_sorted(other, key, maintain_order: false)
|
|
6594
|
+
lazy.merge_sorted(other.lazy, key, maintain_order: maintain_order).collect(optimizations: QueryOptFlags._eager)
|
|
4974
6595
|
end
|
|
4975
6596
|
|
|
4976
|
-
#
|
|
6597
|
+
# Flag a column as sorted.
|
|
6598
|
+
#
|
|
6599
|
+
# This can speed up future operations.
|
|
6600
|
+
#
|
|
6601
|
+
# @note
|
|
6602
|
+
# This can lead to incorrect results if the data is NOT sorted! Use with care!
|
|
4977
6603
|
#
|
|
4978
6604
|
# @param column [Object]
|
|
4979
|
-
#
|
|
6605
|
+
# Column that is sorted.
|
|
4980
6606
|
# @param descending [Boolean]
|
|
4981
|
-
# Whether the
|
|
6607
|
+
# Whether the column is sorted in descending order.
|
|
6608
|
+
# @param nulls_last [Boolean]
|
|
6609
|
+
# Whether the nulls are at the end.
|
|
4982
6610
|
#
|
|
4983
6611
|
# @return [DataFrame]
|
|
4984
6612
|
def set_sorted(
|
|
4985
6613
|
column,
|
|
4986
|
-
descending: false
|
|
6614
|
+
descending: false,
|
|
6615
|
+
nulls_last: false
|
|
4987
6616
|
)
|
|
4988
6617
|
lazy
|
|
4989
|
-
.set_sorted(column, descending: descending)
|
|
4990
|
-
.collect(
|
|
6618
|
+
.set_sorted(column, descending: descending, nulls_last: nulls_last)
|
|
6619
|
+
.collect(optimizations: QueryOptFlags._eager)
|
|
4991
6620
|
end
|
|
4992
6621
|
|
|
4993
|
-
#
|
|
4994
|
-
#
|
|
4995
|
-
#
|
|
6622
|
+
# Update the values in this `DataFrame` with the values in `other`.
|
|
6623
|
+
#
|
|
6624
|
+
# @note
|
|
6625
|
+
# This functionality is considered **unstable**. It may be changed
|
|
6626
|
+
# at any point without it being considered a breaking change.
|
|
6627
|
+
#
|
|
6628
|
+
# @param other [DataFrame]
|
|
6629
|
+
# DataFrame that will be used to update the values
|
|
6630
|
+
# @param on [Object]
|
|
6631
|
+
# Column names that will be joined on. If set to `nil` (default),
|
|
6632
|
+
# the implicit row index of each frame is used as a join key.
|
|
6633
|
+
# @param how ['left', 'inner', 'full']
|
|
6634
|
+
# * 'left' will keep all rows from the left table; rows may be duplicated
|
|
6635
|
+
# if multiple rows in the right frame match the left row's key.
|
|
6636
|
+
# * 'inner' keeps only those rows where the key exists in both frames.
|
|
6637
|
+
# * 'full' will update existing rows where the key matches while also
|
|
6638
|
+
# adding any new rows contained in the given frame.
|
|
6639
|
+
# @param left_on [Object]
|
|
6640
|
+
# Join column(s) of the left DataFrame.
|
|
6641
|
+
# @param right_on [Object]
|
|
6642
|
+
# Join column(s) of the right DataFrame.
|
|
6643
|
+
# @param include_nulls [Boolean]
|
|
6644
|
+
# Overwrite values in the left frame with null values from the right frame.
|
|
6645
|
+
# If set to `false` (default), null values in the right frame are ignored.
|
|
6646
|
+
# @param maintain_order ['none', 'left', 'right', 'left_right', 'right_left']
|
|
6647
|
+
# Which order of rows from the inputs to preserve. See `DataFrame.join`
|
|
6648
|
+
# for details. Unlike `join` this function preserves the left order by
|
|
6649
|
+
# default.
|
|
6650
|
+
#
|
|
6651
|
+
# @return [DataFrame]
|
|
6652
|
+
#
|
|
6653
|
+
# @note
|
|
6654
|
+
# This is syntactic sugar for a left/inner join that preserves the order
|
|
6655
|
+
# of the left `DataFrame` by default, with an optional coalesce when
|
|
6656
|
+
# `include_nulls: false`.
|
|
6657
|
+
#
|
|
6658
|
+
# @example Update `df` values with the non-null values in `new_df`, by row index:
|
|
6659
|
+
# df = Polars::DataFrame.new(
|
|
6660
|
+
# {
|
|
6661
|
+
# "A" => [1, 2, 3, 4],
|
|
6662
|
+
# "B" => [400, 500, 600, 700]
|
|
6663
|
+
# }
|
|
6664
|
+
# )
|
|
6665
|
+
# new_df = Polars::DataFrame.new(
|
|
6666
|
+
# {
|
|
6667
|
+
# "B" => [-66, nil, -99],
|
|
6668
|
+
# "C" => [5, 3, 1]
|
|
6669
|
+
# }
|
|
6670
|
+
# )
|
|
6671
|
+
# df.update(new_df)
|
|
6672
|
+
# # =>
|
|
6673
|
+
# # shape: (4, 2)
|
|
6674
|
+
# # ┌─────┬─────┐
|
|
6675
|
+
# # │ A ┆ B │
|
|
6676
|
+
# # │ --- ┆ --- │
|
|
6677
|
+
# # │ i64 ┆ i64 │
|
|
6678
|
+
# # ╞═════╪═════╡
|
|
6679
|
+
# # │ 1 ┆ -66 │
|
|
6680
|
+
# # │ 2 ┆ 500 │
|
|
6681
|
+
# # │ 3 ┆ -99 │
|
|
6682
|
+
# # │ 4 ┆ 700 │
|
|
6683
|
+
# # └─────┴─────┘
|
|
6684
|
+
#
|
|
6685
|
+
# @example Update `df` values with the non-null values in `new_df`, by row index, but only keeping those rows that are common to both frames:
|
|
6686
|
+
# df.update(new_df, how: "inner")
|
|
6687
|
+
# # =>
|
|
6688
|
+
# # shape: (3, 2)
|
|
6689
|
+
# # ┌─────┬─────┐
|
|
6690
|
+
# # │ A ┆ B │
|
|
6691
|
+
# # │ --- ┆ --- │
|
|
6692
|
+
# # │ i64 ┆ i64 │
|
|
6693
|
+
# # ╞═════╪═════╡
|
|
6694
|
+
# # │ 1 ┆ -66 │
|
|
6695
|
+
# # │ 2 ┆ 500 │
|
|
6696
|
+
# # │ 3 ┆ -99 │
|
|
6697
|
+
# # └─────┴─────┘
|
|
6698
|
+
#
|
|
6699
|
+
# @example Update `df` values with the non-null values in `new_df`, using a full outer join strategy that defines explicit join columns in each frame:
|
|
6700
|
+
# df.update(new_df, left_on: ["A"], right_on: ["C"], how: "full")
|
|
6701
|
+
# # =>
|
|
6702
|
+
# # shape: (5, 2)
|
|
6703
|
+
# # ┌─────┬─────┐
|
|
6704
|
+
# # │ A ┆ B │
|
|
6705
|
+
# # │ --- ┆ --- │
|
|
6706
|
+
# # │ i64 ┆ i64 │
|
|
6707
|
+
# # ╞═════╪═════╡
|
|
6708
|
+
# # │ 1 ┆ -99 │
|
|
6709
|
+
# # │ 2 ┆ 500 │
|
|
6710
|
+
# # │ 3 ┆ 600 │
|
|
6711
|
+
# # │ 4 ┆ 700 │
|
|
6712
|
+
# # │ 5 ┆ -66 │
|
|
6713
|
+
# # └─────┴─────┘
|
|
6714
|
+
#
|
|
6715
|
+
# @example Update `df` values including null values in `new_df`, using a full outer join strategy that defines explicit join columns in each frame:
|
|
6716
|
+
# df.update(new_df, left_on: "A", right_on: "C", how: "full", include_nulls: true)
|
|
6717
|
+
# # =>
|
|
6718
|
+
# # shape: (5, 2)
|
|
6719
|
+
# # ┌─────┬──────┐
|
|
6720
|
+
# # │ A ┆ B │
|
|
6721
|
+
# # │ --- ┆ --- │
|
|
6722
|
+
# # │ i64 ┆ i64 │
|
|
6723
|
+
# # ╞═════╪══════╡
|
|
6724
|
+
# # │ 1 ┆ -99 │
|
|
6725
|
+
# # │ 2 ┆ 500 │
|
|
6726
|
+
# # │ 3 ┆ null │
|
|
6727
|
+
# # │ 4 ┆ 700 │
|
|
6728
|
+
# # │ 5 ┆ -66 │
|
|
6729
|
+
# # └─────┴──────┘
|
|
6730
|
+
def update(
|
|
6731
|
+
other,
|
|
6732
|
+
on: nil,
|
|
6733
|
+
how: "left",
|
|
6734
|
+
left_on: nil,
|
|
6735
|
+
right_on: nil,
|
|
6736
|
+
include_nulls: false,
|
|
6737
|
+
maintain_order: "left"
|
|
6738
|
+
)
|
|
6739
|
+
Utils.require_same_type(self, other)
|
|
6740
|
+
lazy
|
|
6741
|
+
.update(
|
|
6742
|
+
other.lazy,
|
|
6743
|
+
on: on,
|
|
6744
|
+
how: how,
|
|
6745
|
+
left_on: left_on,
|
|
6746
|
+
right_on: right_on,
|
|
6747
|
+
include_nulls: include_nulls,
|
|
6748
|
+
maintain_order: maintain_order
|
|
6749
|
+
)
|
|
6750
|
+
.collect(optimizations: QueryOptFlags._eager)
|
|
6751
|
+
end
|
|
6752
|
+
|
|
6753
|
+
# Match or evolve the schema of a LazyFrame into a specific schema.
|
|
6754
|
+
#
|
|
6755
|
+
# By default, match_to_schema returns an error if the input schema does not
|
|
6756
|
+
# exactly match the target schema. It also allows columns to be freely reordered,
|
|
6757
|
+
# with additional coercion rules available through optional parameters.
|
|
6758
|
+
#
|
|
6759
|
+
# @note
|
|
6760
|
+
# This functionality is considered **unstable**. It may be changed
|
|
6761
|
+
# at any point without it being considered a breaking change.
|
|
6762
|
+
#
|
|
6763
|
+
# @param schema [Object]
|
|
6764
|
+
# Target schema to match or evolve to.
|
|
6765
|
+
# @param missing_columns [Object]
|
|
6766
|
+
# Raise of insert missing columns from the input with respect to the `schema`.
|
|
6767
|
+
#
|
|
6768
|
+
# This can also be an expression per column with what to insert if it is
|
|
6769
|
+
# missing.
|
|
6770
|
+
# @param missing_struct_fields [Object]
|
|
6771
|
+
# Raise of insert missing struct fields from the input with respect to the
|
|
6772
|
+
# `schema`.
|
|
6773
|
+
# @param extra_columns [Object]
|
|
6774
|
+
# Raise of ignore extra columns from the input with respect to the `schema`.
|
|
6775
|
+
# @param extra_struct_fields [Object]
|
|
6776
|
+
# Raise of ignore extra struct fields from the input with respect to the
|
|
6777
|
+
# `schema`.
|
|
6778
|
+
# @param integer_cast [Object]
|
|
6779
|
+
# Forbid of upcast for integer columns from the input to the respective column
|
|
6780
|
+
# in `schema`.
|
|
6781
|
+
# @param float_cast [Object]
|
|
6782
|
+
# Forbid of upcast for float columns from the input to the respective column
|
|
6783
|
+
# in `schema`.
|
|
6784
|
+
#
|
|
6785
|
+
# @return [DataFrame]
|
|
6786
|
+
#
|
|
6787
|
+
# @example Ensuring the schema matches
|
|
6788
|
+
# df = Polars::DataFrame.new({"a" => [1, 2, 3], "b" => ["A", "B", "C"]})
|
|
6789
|
+
# df.match_to_schema({"a" => Polars::Int64, "b" => Polars::String})
|
|
6790
|
+
# # =>
|
|
6791
|
+
# # shape: (3, 2)
|
|
6792
|
+
# # ┌─────┬─────┐
|
|
6793
|
+
# # │ a ┆ b │
|
|
6794
|
+
# # │ --- ┆ --- │
|
|
6795
|
+
# # │ i64 ┆ str │
|
|
6796
|
+
# # ╞═════╪═════╡
|
|
6797
|
+
# # │ 1 ┆ A │
|
|
6798
|
+
# # │ 2 ┆ B │
|
|
6799
|
+
# # │ 3 ┆ C │
|
|
6800
|
+
# # └─────┴─────┘
|
|
6801
|
+
def match_to_schema(
|
|
6802
|
+
schema,
|
|
6803
|
+
missing_columns: "raise",
|
|
6804
|
+
missing_struct_fields: "raise",
|
|
6805
|
+
extra_columns: "raise",
|
|
6806
|
+
extra_struct_fields: "raise",
|
|
6807
|
+
integer_cast: "forbid",
|
|
6808
|
+
float_cast: "forbid"
|
|
6809
|
+
)
|
|
6810
|
+
lazy
|
|
6811
|
+
.match_to_schema(
|
|
6812
|
+
schema,
|
|
6813
|
+
missing_columns: missing_columns,
|
|
6814
|
+
missing_struct_fields: missing_struct_fields,
|
|
6815
|
+
extra_columns: extra_columns,
|
|
6816
|
+
extra_struct_fields: extra_struct_fields,
|
|
6817
|
+
integer_cast: integer_cast,
|
|
6818
|
+
float_cast: float_cast
|
|
6819
|
+
)
|
|
6820
|
+
.collect(optimizations: QueryOptFlags._eager)
|
|
6821
|
+
end
|
|
4996
6822
|
|
|
4997
6823
|
private
|
|
4998
6824
|
|
|
@@ -5057,282 +6883,6 @@ module Polars
|
|
|
5057
6883
|
raise ArgumentError, "Unsupported idxs datatype."
|
|
5058
6884
|
end
|
|
5059
6885
|
|
|
5060
|
-
# @private
|
|
5061
|
-
def self.expand_hash_scalars(data, schema_overrides: nil, strict: true, order: nil, nan_to_null: false)
|
|
5062
|
-
updated_data = {}
|
|
5063
|
-
unless data.empty?
|
|
5064
|
-
dtypes = schema_overrides || {}
|
|
5065
|
-
array_len = data.values.map { |val| Utils.arrlen(val) || 0 }.max
|
|
5066
|
-
if array_len > 0
|
|
5067
|
-
data.each do |name, val|
|
|
5068
|
-
dtype = dtypes[name]
|
|
5069
|
-
if val.is_a?(Hash) && dtype != Struct
|
|
5070
|
-
updated_data[name] = DataFrame.new(val, strict: strict).to_struct(name)
|
|
5071
|
-
elsif !Utils.arrlen(val).nil?
|
|
5072
|
-
updated_data[name] = Series.new(::String.new(name), val, dtype: dtype, strict: strict)
|
|
5073
|
-
elsif val.nil? || [Integer, Float, TrueClass, FalseClass, ::String, ::Date, ::DateTime, ::Time].any? { |cls| val.is_a?(cls) }
|
|
5074
|
-
dtype = Polars::Float64 if val.nil? && dtype.nil?
|
|
5075
|
-
updated_data[name] = Series.new(::String.new(name), [val], dtype: dtype, strict: strict).extend_constant(val, array_len - 1)
|
|
5076
|
-
else
|
|
5077
|
-
raise Todo
|
|
5078
|
-
end
|
|
5079
|
-
end
|
|
5080
|
-
elsif data.values.all? { |val| Utils.arrlen(val) == 0 }
|
|
5081
|
-
data.each do |name, val|
|
|
5082
|
-
updated_data[name] = Series.new(name, val, dtype: dtypes[name], strict: strict)
|
|
5083
|
-
end
|
|
5084
|
-
elsif data.values.all? { |val| Utils.arrlen(val).nil? }
|
|
5085
|
-
data.each do |name, val|
|
|
5086
|
-
updated_data[name] = Series.new(name, [val], dtype: dtypes[name], strict: strict)
|
|
5087
|
-
end
|
|
5088
|
-
end
|
|
5089
|
-
end
|
|
5090
|
-
updated_data
|
|
5091
|
-
end
|
|
5092
|
-
|
|
5093
|
-
# @private
|
|
5094
|
-
def self.hash_to_rbdf(data, schema: nil, schema_overrides: nil, strict: true, nan_to_null: nil)
|
|
5095
|
-
if schema.is_a?(Hash) && !data.empty?
|
|
5096
|
-
if !data.all? { |col, _| schema[col] }
|
|
5097
|
-
raise ArgumentError, "The given column-schema names do not match the data dictionary"
|
|
5098
|
-
end
|
|
5099
|
-
|
|
5100
|
-
data = schema.to_h { |col| [col, data[col]] }
|
|
5101
|
-
end
|
|
5102
|
-
|
|
5103
|
-
column_names, schema_overrides = _unpack_schema(
|
|
5104
|
-
schema, lookup_names: data.keys, schema_overrides: schema_overrides
|
|
5105
|
-
)
|
|
5106
|
-
if column_names.empty?
|
|
5107
|
-
column_names = data.keys
|
|
5108
|
-
end
|
|
5109
|
-
|
|
5110
|
-
if data.empty? && !schema_overrides.empty?
|
|
5111
|
-
data_series = column_names.map { |name| Series.new(name, [], dtype: schema_overrides[name], strict: strict, nan_to_null: nan_to_null)._s }
|
|
5112
|
-
else
|
|
5113
|
-
data_series = expand_hash_scalars(data, schema_overrides: schema_overrides, strict: strict, nan_to_null: nan_to_null).values.map(&:_s)
|
|
5114
|
-
end
|
|
5115
|
-
|
|
5116
|
-
data_series = _handle_columns_arg(data_series, columns: column_names, from_hash: true)
|
|
5117
|
-
RbDataFrame.new(data_series)
|
|
5118
|
-
end
|
|
5119
|
-
|
|
5120
|
-
# @private
|
|
5121
|
-
def self.include_unknowns(schema, cols)
|
|
5122
|
-
cols.to_h { |col| [col, schema.fetch(col, Unknown)] }
|
|
5123
|
-
end
|
|
5124
|
-
|
|
5125
|
-
# @private
|
|
5126
|
-
def self._unpack_schema(schema, schema_overrides: nil, n_expected: nil, lookup_names: nil, include_overrides_in_columns: false)
|
|
5127
|
-
if schema.is_a?(Hash)
|
|
5128
|
-
schema = schema.to_a
|
|
5129
|
-
end
|
|
5130
|
-
column_names =
|
|
5131
|
-
(schema || []).map.with_index do |col, i|
|
|
5132
|
-
if col.is_a?(::String)
|
|
5133
|
-
col || "column_#{i}"
|
|
5134
|
-
else
|
|
5135
|
-
col[0]
|
|
5136
|
-
end
|
|
5137
|
-
end
|
|
5138
|
-
if column_names.empty? && n_expected
|
|
5139
|
-
column_names = n_expected.times.map { |i| "column_#{i}" }
|
|
5140
|
-
end
|
|
5141
|
-
# TODO zip_longest
|
|
5142
|
-
lookup = column_names.zip(lookup_names || []).to_h
|
|
5143
|
-
|
|
5144
|
-
column_dtypes =
|
|
5145
|
-
(schema || []).select { |col| !col.is_a?(::String) && col[1] }.to_h do |col|
|
|
5146
|
-
[lookup[col[0]] || col[0], col[1]]
|
|
5147
|
-
end
|
|
5148
|
-
|
|
5149
|
-
if schema_overrides && schema_overrides.any?
|
|
5150
|
-
column_dtypes.merge!(schema_overrides)
|
|
5151
|
-
end
|
|
5152
|
-
|
|
5153
|
-
column_dtypes.each do |col, dtype|
|
|
5154
|
-
if !Utils.is_polars_dtype(dtype, include_unknown: true) && !dtype.nil?
|
|
5155
|
-
column_dtypes[col] = Utils.rb_type_to_dtype(dtype)
|
|
5156
|
-
end
|
|
5157
|
-
end
|
|
5158
|
-
|
|
5159
|
-
[column_names, column_dtypes]
|
|
5160
|
-
end
|
|
5161
|
-
|
|
5162
|
-
def self._handle_columns_arg(data, columns: nil, from_hash: false)
|
|
5163
|
-
if columns.nil? || columns.empty?
|
|
5164
|
-
data
|
|
5165
|
-
else
|
|
5166
|
-
if data.empty?
|
|
5167
|
-
columns.map { |c| Series.new(c, nil)._s }
|
|
5168
|
-
elsif data.length == columns.length
|
|
5169
|
-
if from_hash
|
|
5170
|
-
series_map = data.to_h { |s| [s.name, s] }
|
|
5171
|
-
if columns.all? { |col| series_map.key?(col) }
|
|
5172
|
-
return columns.map { |col| series_map[col] }
|
|
5173
|
-
end
|
|
5174
|
-
end
|
|
5175
|
-
|
|
5176
|
-
columns.each_with_index do |c, i|
|
|
5177
|
-
# not in-place?
|
|
5178
|
-
data[i].rename(c)
|
|
5179
|
-
end
|
|
5180
|
-
data
|
|
5181
|
-
else
|
|
5182
|
-
raise ArgumentError, "Dimensions of columns arg must match data dimensions."
|
|
5183
|
-
end
|
|
5184
|
-
end
|
|
5185
|
-
end
|
|
5186
|
-
|
|
5187
|
-
def self._post_apply_columns(rbdf, columns, structs: nil, schema_overrides: nil, strict: true)
|
|
5188
|
-
rbdf_columns = rbdf.columns
|
|
5189
|
-
rbdf_dtypes = rbdf.dtypes
|
|
5190
|
-
columns, dtypes = _unpack_schema(
|
|
5191
|
-
(columns || rbdf_columns), schema_overrides: schema_overrides
|
|
5192
|
-
)
|
|
5193
|
-
column_subset = []
|
|
5194
|
-
if columns != rbdf_columns
|
|
5195
|
-
if columns.length < rbdf_columns.length && columns == rbdf_columns.first(columns.length)
|
|
5196
|
-
column_subset = columns
|
|
5197
|
-
else
|
|
5198
|
-
rbdf.set_column_names(columns)
|
|
5199
|
-
end
|
|
5200
|
-
end
|
|
5201
|
-
|
|
5202
|
-
column_casts = []
|
|
5203
|
-
columns.each_with_index do |col, i|
|
|
5204
|
-
if dtypes[col] == Categorical # != rbdf_dtypes[i]
|
|
5205
|
-
column_casts << Polars.col(col).cast(Categorical, strict: strict)._rbexpr
|
|
5206
|
-
elsif structs&.any? && structs.include?(col) && structs[col] != rbdf_dtypes[i]
|
|
5207
|
-
column_casts << Polars.col(col).cast(structs[col], strict: strict)._rbexpr
|
|
5208
|
-
elsif dtypes.include?(col) && dtypes[col] != rbdf_dtypes[i]
|
|
5209
|
-
column_casts << Polars.col(col).cast(dtypes[col], strict: strict)._rbexpr
|
|
5210
|
-
end
|
|
5211
|
-
end
|
|
5212
|
-
|
|
5213
|
-
if column_casts.any? || column_subset.any?
|
|
5214
|
-
rbdf = rbdf.lazy
|
|
5215
|
-
if column_casts.any?
|
|
5216
|
-
rbdf = rbdf.with_columns(column_casts)
|
|
5217
|
-
end
|
|
5218
|
-
if column_subset.any?
|
|
5219
|
-
rbdf = rbdf.select(column_subset.map { |col| Polars.col(col)._rbexpr })
|
|
5220
|
-
end
|
|
5221
|
-
rbdf = rbdf.collect
|
|
5222
|
-
end
|
|
5223
|
-
|
|
5224
|
-
rbdf
|
|
5225
|
-
end
|
|
5226
|
-
|
|
5227
|
-
# @private
|
|
5228
|
-
def self.sequence_to_rbdf(data, schema: nil, schema_overrides: nil, strict: true, orient: nil, infer_schema_length: 50)
|
|
5229
|
-
columns = schema
|
|
5230
|
-
|
|
5231
|
-
if data.length == 0
|
|
5232
|
-
return hash_to_rbdf({}, schema: schema, schema_overrides: schema_overrides, strict: strict)
|
|
5233
|
-
end
|
|
5234
|
-
|
|
5235
|
-
if data[0].is_a?(Series)
|
|
5236
|
-
# series_names = data.map(&:name)
|
|
5237
|
-
# columns, dtypes = _unpack_schema(columns || series_names, n_expected: data.length)
|
|
5238
|
-
data_series = []
|
|
5239
|
-
data.each do |s|
|
|
5240
|
-
data_series << s._s
|
|
5241
|
-
end
|
|
5242
|
-
elsif data[0].is_a?(Hash)
|
|
5243
|
-
column_names, dtypes = _unpack_schema(columns)
|
|
5244
|
-
schema_overrides = dtypes ? include_unknowns(dtypes, column_names) : nil
|
|
5245
|
-
rbdf = RbDataFrame.from_hashes(data, schema, schema_overrides, strict, infer_schema_length)
|
|
5246
|
-
if column_names
|
|
5247
|
-
rbdf = _post_apply_columns(rbdf, column_names)
|
|
5248
|
-
end
|
|
5249
|
-
return rbdf
|
|
5250
|
-
elsif data[0].is_a?(::Array)
|
|
5251
|
-
first_element = data[0]
|
|
5252
|
-
if orient.nil? && !columns.nil?
|
|
5253
|
-
row_types = first_element.filter_map { |value| value.class }.uniq
|
|
5254
|
-
if row_types.include?(Integer) && row_types.include?(Float)
|
|
5255
|
-
row_types.delete(Integer)
|
|
5256
|
-
end
|
|
5257
|
-
orient = row_types.length == 1 ? "col" : "row"
|
|
5258
|
-
end
|
|
5259
|
-
|
|
5260
|
-
if orient == "row"
|
|
5261
|
-
column_names, schema_overrides = _unpack_schema(
|
|
5262
|
-
schema, schema_overrides: schema_overrides, n_expected: first_element.length
|
|
5263
|
-
)
|
|
5264
|
-
local_schema_override = (
|
|
5265
|
-
schema_overrides.any? ? _include_unknowns(schema_overrides, column_names) : {}
|
|
5266
|
-
)
|
|
5267
|
-
if column_names.any? && first_element.length > 0 && first_element.length != column_names.length
|
|
5268
|
-
raise ArgumentError, "the row data does not match the number of columns"
|
|
5269
|
-
end
|
|
5270
|
-
|
|
5271
|
-
unpack_nested = false
|
|
5272
|
-
local_schema_override.each do |col, tp|
|
|
5273
|
-
if [Categorical, Enum].include?(tp)
|
|
5274
|
-
local_schema_override[col] = String
|
|
5275
|
-
elsif !unpack_nested && [Unknown, Struct].include?(tp.base_type)
|
|
5276
|
-
raise Todo
|
|
5277
|
-
end
|
|
5278
|
-
end
|
|
5279
|
-
|
|
5280
|
-
if unpack_nested
|
|
5281
|
-
raise Todo
|
|
5282
|
-
else
|
|
5283
|
-
rbdf = RbDataFrame.from_rows(
|
|
5284
|
-
data,
|
|
5285
|
-
infer_schema_length,
|
|
5286
|
-
local_schema_override.any? ? local_schema_override : nil
|
|
5287
|
-
)
|
|
5288
|
-
end
|
|
5289
|
-
if column_names.any? || schema_overrides.any?
|
|
5290
|
-
rbdf = _post_apply_columns(
|
|
5291
|
-
rbdf, column_names, schema_overrides: schema_overrides, strict: strict
|
|
5292
|
-
)
|
|
5293
|
-
end
|
|
5294
|
-
return rbdf
|
|
5295
|
-
elsif orient == "col" || orient.nil?
|
|
5296
|
-
column_names, schema_overrides = _unpack_schema(
|
|
5297
|
-
schema, schema_overrides: schema_overrides, n_expected: data.length
|
|
5298
|
-
)
|
|
5299
|
-
data_series =
|
|
5300
|
-
data.map.with_index do |element, i|
|
|
5301
|
-
Series.new(column_names[i], element, dtype: schema_overrides[column_names[i]], strict: strict)._s
|
|
5302
|
-
end
|
|
5303
|
-
return RbDataFrame.new(data_series)
|
|
5304
|
-
else
|
|
5305
|
-
raise ArgumentError, "orient must be one of {{'col', 'row', nil}}, got #{orient} instead."
|
|
5306
|
-
end
|
|
5307
|
-
end
|
|
5308
|
-
|
|
5309
|
-
data_series = _handle_columns_arg(data_series, columns: columns)
|
|
5310
|
-
RbDataFrame.new(data_series)
|
|
5311
|
-
end
|
|
5312
|
-
|
|
5313
|
-
# @private
|
|
5314
|
-
def self._include_unknowns(schema, cols)
|
|
5315
|
-
cols.to_h { |col| [col, schema[col] || Unknown] }
|
|
5316
|
-
end
|
|
5317
|
-
|
|
5318
|
-
# @private
|
|
5319
|
-
def self.series_to_rbdf(data, schema: nil, schema_overrides: nil, strict: true)
|
|
5320
|
-
data_series = [data._s]
|
|
5321
|
-
series_name = data_series.map(&:name)
|
|
5322
|
-
column_names, schema_overrides = _unpack_schema(
|
|
5323
|
-
schema || series_name, schema_overrides: schema_overrides, n_expected: 1
|
|
5324
|
-
)
|
|
5325
|
-
if schema_overrides.any?
|
|
5326
|
-
new_dtype = schema_overrides.values[0]
|
|
5327
|
-
if new_dtype != data.dtype
|
|
5328
|
-
data_series[0] = data_series[0].cast(new_dtype, strict)
|
|
5329
|
-
end
|
|
5330
|
-
end
|
|
5331
|
-
|
|
5332
|
-
data_series = _handle_columns_arg(data_series, columns: column_names)
|
|
5333
|
-
RbDataFrame.new(data_series)
|
|
5334
|
-
end
|
|
5335
|
-
|
|
5336
6886
|
def wrap_ldf(ldf)
|
|
5337
6887
|
LazyFrame._from_rbldf(ldf)
|
|
5338
6888
|
end
|
|
@@ -5341,6 +6891,11 @@ module Polars
|
|
|
5341
6891
|
self.class._from_rbdf(rb_df)
|
|
5342
6892
|
end
|
|
5343
6893
|
|
|
6894
|
+
def _replace(column, new_column)
|
|
6895
|
+
self._df.replace(column, new_column._s)
|
|
6896
|
+
self
|
|
6897
|
+
end
|
|
6898
|
+
|
|
5344
6899
|
def _comp(other, op)
|
|
5345
6900
|
if other.is_a?(DataFrame)
|
|
5346
6901
|
_compare_to_other_df(other, op)
|
|
@@ -5358,7 +6913,7 @@ module Polars
|
|
|
5358
6913
|
end
|
|
5359
6914
|
|
|
5360
6915
|
suffix = "__POLARS_CMP_OTHER"
|
|
5361
|
-
other_renamed = other.select(Polars.all.suffix(suffix))
|
|
6916
|
+
other_renamed = other.select(Polars.all.name.suffix(suffix))
|
|
5362
6917
|
combined = Polars.concat([self, other_renamed], how: "horizontal")
|
|
5363
6918
|
|
|
5364
6919
|
expr = case op
|
|
@@ -5410,5 +6965,284 @@ module Polars
|
|
|
5410
6965
|
end
|
|
5411
6966
|
other
|
|
5412
6967
|
end
|
|
6968
|
+
|
|
6969
|
+
def with_connection(connection, &block)
|
|
6970
|
+
if !connection.nil?
|
|
6971
|
+
yield connection
|
|
6972
|
+
else
|
|
6973
|
+
ActiveRecord::Base.connection_pool.with_connection(&block)
|
|
6974
|
+
end
|
|
6975
|
+
end
|
|
6976
|
+
|
|
6977
|
+
def maybe_transaction(connection, create_table, &block)
|
|
6978
|
+
if create_table && connection.adapter_name.match?(/postg|sqlite/i) && connection.open_transactions == 0
|
|
6979
|
+
connection.transaction(&block)
|
|
6980
|
+
else
|
|
6981
|
+
yield
|
|
6982
|
+
end
|
|
6983
|
+
end
|
|
6984
|
+
|
|
6985
|
+
def get_series_item_by_key(s, key)
|
|
6986
|
+
if key.is_a?(Integer)
|
|
6987
|
+
return s._s.get_index_signed(key)
|
|
6988
|
+
|
|
6989
|
+
elsif key.is_a?(Range)
|
|
6990
|
+
return _select_elements_by_slice(s, key)
|
|
6991
|
+
|
|
6992
|
+
elsif key.is_a?(::Array)
|
|
6993
|
+
if key.empty?
|
|
6994
|
+
return s.clear
|
|
6995
|
+
end
|
|
6996
|
+
|
|
6997
|
+
first = key[0]
|
|
6998
|
+
if Utils.bool?(first)
|
|
6999
|
+
_raise_on_boolean_mask
|
|
7000
|
+
end
|
|
7001
|
+
|
|
7002
|
+
begin
|
|
7003
|
+
indices = Series.new("", key, dtype: Int64)
|
|
7004
|
+
rescue TypeError
|
|
7005
|
+
msg = "cannot select elements using Sequence with elements of type #{first.class.name.inspect}"
|
|
7006
|
+
raise TypeError, msg
|
|
7007
|
+
end
|
|
7008
|
+
|
|
7009
|
+
indices = _convert_series_to_indices(indices, s.len)
|
|
7010
|
+
return _select_elements_by_index(s, indices)
|
|
7011
|
+
|
|
7012
|
+
elsif key.is_a?(Series)
|
|
7013
|
+
indices = _convert_series_to_indices(key, s.len)
|
|
7014
|
+
return _select_elements_by_index(s, indices)
|
|
7015
|
+
end
|
|
7016
|
+
|
|
7017
|
+
msg = "cannot select elements using key of type #{key.class.name.inspect}: #{key.inspect}"
|
|
7018
|
+
raise TypeError, msg
|
|
7019
|
+
end
|
|
7020
|
+
|
|
7021
|
+
def _select_elements_by_slice(s, key)
|
|
7022
|
+
Slice.new(s).apply(key)
|
|
7023
|
+
end
|
|
7024
|
+
|
|
7025
|
+
def _select_elements_by_index(s, key)
|
|
7026
|
+
s.send(:_from_rbseries, s._s.gather_with_series(key._s))
|
|
7027
|
+
end
|
|
7028
|
+
|
|
7029
|
+
def get_df_item_by_key(df, key)
|
|
7030
|
+
if key.size == 2
|
|
7031
|
+
row_key, col_key = key
|
|
7032
|
+
|
|
7033
|
+
# Support df[True, False] and df["a", "b"] as these are not ambiguous
|
|
7034
|
+
if Utils.bool?(row_key) || Utils.strlike?(row_key)
|
|
7035
|
+
return _select_columns(df, key)
|
|
7036
|
+
end
|
|
7037
|
+
|
|
7038
|
+
selection = _select_columns(df, col_key)
|
|
7039
|
+
|
|
7040
|
+
if selection.is_empty
|
|
7041
|
+
return selection
|
|
7042
|
+
elsif selection.is_a?(Series)
|
|
7043
|
+
return get_series_item_by_key(selection, row_key)
|
|
7044
|
+
else
|
|
7045
|
+
return _select_rows(selection, row_key)
|
|
7046
|
+
end
|
|
7047
|
+
end
|
|
7048
|
+
|
|
7049
|
+
key = key[0] if key.size == 1
|
|
7050
|
+
|
|
7051
|
+
# Single string input, e.g. df["a"]
|
|
7052
|
+
if Utils.strlike?(key)
|
|
7053
|
+
# This case is required because empty strings are otherwise treated
|
|
7054
|
+
# as an empty Sequence in `_select_rows`
|
|
7055
|
+
return df.get_column(key)
|
|
7056
|
+
end
|
|
7057
|
+
|
|
7058
|
+
# Single input - df[1] - or multiple inputs - df["a", "b", "c"]
|
|
7059
|
+
begin
|
|
7060
|
+
_select_rows(df, key)
|
|
7061
|
+
rescue TypeError
|
|
7062
|
+
_select_columns(df, key)
|
|
7063
|
+
end
|
|
7064
|
+
end
|
|
7065
|
+
|
|
7066
|
+
def _select_columns(df, key)
|
|
7067
|
+
if key.is_a?(Integer)
|
|
7068
|
+
return df.to_series(key)
|
|
7069
|
+
|
|
7070
|
+
elsif Utils.strlike?(key)
|
|
7071
|
+
return df.get_column(key)
|
|
7072
|
+
|
|
7073
|
+
elsif key.is_a?(Range)
|
|
7074
|
+
start, stop = key.begin, key.end
|
|
7075
|
+
if start.is_a?(::String)
|
|
7076
|
+
start = df.get_column_index(start)
|
|
7077
|
+
stop = df.get_column_index(stop)
|
|
7078
|
+
rng = Range.new(start, stop, key.exclude_end?)
|
|
7079
|
+
return _select_columns_by_index(df, rng)
|
|
7080
|
+
else
|
|
7081
|
+
return _select_columns_by_index(df, key)
|
|
7082
|
+
end
|
|
7083
|
+
|
|
7084
|
+
elsif key.is_a?(::Array)
|
|
7085
|
+
if key.empty?
|
|
7086
|
+
return df.class.new
|
|
7087
|
+
end
|
|
7088
|
+
first = key[0]
|
|
7089
|
+
if Utils.bool?(first)
|
|
7090
|
+
return _select_columns_by_mask(df, key)
|
|
7091
|
+
elsif first.is_a?(Integer)
|
|
7092
|
+
return _select_columns_by_index(df, key)
|
|
7093
|
+
elsif Utils.strlike?(first)
|
|
7094
|
+
return _select_columns_by_name(df, key)
|
|
7095
|
+
else
|
|
7096
|
+
msg = "cannot select columns using Sequence with elements of type #{first.class.name.inspect}"
|
|
7097
|
+
raise TypeError, msg
|
|
7098
|
+
end
|
|
7099
|
+
|
|
7100
|
+
elsif key.is_a?(Series)
|
|
7101
|
+
if key.is_empty
|
|
7102
|
+
return df.class.new
|
|
7103
|
+
end
|
|
7104
|
+
dtype = key.dtype
|
|
7105
|
+
if dtype == String
|
|
7106
|
+
return _select_columns_by_name(df, key)
|
|
7107
|
+
elsif dtype.integer?
|
|
7108
|
+
return _select_columns_by_index(df, key)
|
|
7109
|
+
elsif dtype == Boolean
|
|
7110
|
+
return _select_columns_by_mask(df, key)
|
|
7111
|
+
else
|
|
7112
|
+
msg = "cannot select columns using Series of type #{dtype}"
|
|
7113
|
+
raise TypeError, msg
|
|
7114
|
+
end
|
|
7115
|
+
end
|
|
7116
|
+
|
|
7117
|
+
msg = (
|
|
7118
|
+
"cannot select columns using key of type #{key.class.name.inspect}: #{key.inspect}"
|
|
7119
|
+
)
|
|
7120
|
+
raise TypeError, msg
|
|
7121
|
+
end
|
|
7122
|
+
|
|
7123
|
+
def _select_columns_by_index(df, key)
|
|
7124
|
+
series = key.map { |i| df.to_series(i) }
|
|
7125
|
+
df.class.new(series)
|
|
7126
|
+
end
|
|
7127
|
+
|
|
7128
|
+
def _select_columns_by_name(df, key)
|
|
7129
|
+
df.send(:_from_rbdf, df._df.select(Array(key)))
|
|
7130
|
+
end
|
|
7131
|
+
|
|
7132
|
+
def _select_columns_by_mask(df, key)
|
|
7133
|
+
if key.length != df.width
|
|
7134
|
+
msg = "expected #{df.width} values when selecting columns by boolean mask, got #{key.length}"
|
|
7135
|
+
raise ArgumentError, msg
|
|
7136
|
+
end
|
|
7137
|
+
|
|
7138
|
+
indices = key.each_with_index.filter_map { |val, i| i if val }
|
|
7139
|
+
_select_columns_by_index(df, indices)
|
|
7140
|
+
end
|
|
7141
|
+
|
|
7142
|
+
def _select_rows(df, key)
|
|
7143
|
+
if key.is_a?(Integer)
|
|
7144
|
+
num_rows = df.height
|
|
7145
|
+
if key >= num_rows || key < -num_rows
|
|
7146
|
+
msg = "index #{key} is out of bounds for DataFrame of height #{num_rows}"
|
|
7147
|
+
raise IndexError, msg
|
|
7148
|
+
end
|
|
7149
|
+
return df.slice(key, 1)
|
|
7150
|
+
end
|
|
7151
|
+
|
|
7152
|
+
if key.is_a?(Range)
|
|
7153
|
+
return _select_rows_by_slice(df, key)
|
|
7154
|
+
|
|
7155
|
+
elsif key.is_a?(::Array)
|
|
7156
|
+
if key.empty?
|
|
7157
|
+
return df.clear
|
|
7158
|
+
end
|
|
7159
|
+
if Utils.bool?(key[0])
|
|
7160
|
+
_raise_on_boolean_mask
|
|
7161
|
+
end
|
|
7162
|
+
s = Series.new("", key, dtype: Int64)
|
|
7163
|
+
indices = _convert_series_to_indices(s, df.height)
|
|
7164
|
+
return _select_rows_by_index(df, indices)
|
|
7165
|
+
|
|
7166
|
+
elsif key.is_a?(Series)
|
|
7167
|
+
indices = _convert_series_to_indices(key, df.height)
|
|
7168
|
+
return _select_rows_by_index(df, indices)
|
|
7169
|
+
|
|
7170
|
+
else
|
|
7171
|
+
msg = "cannot select rows using key of type #{key.class.name.inspect}: #{key.inspect}"
|
|
7172
|
+
raise TypeError, msg
|
|
7173
|
+
end
|
|
7174
|
+
end
|
|
7175
|
+
|
|
7176
|
+
def _select_rows_by_slice(df, key)
|
|
7177
|
+
Slice.new(df).apply(key)
|
|
7178
|
+
end
|
|
7179
|
+
|
|
7180
|
+
def _select_rows_by_index(df, key)
|
|
7181
|
+
df.send(:_from_rbdf, df._df.gather_with_series(key._s))
|
|
7182
|
+
end
|
|
7183
|
+
|
|
7184
|
+
def _convert_series_to_indices(s, size)
|
|
7185
|
+
idx_type = Plr.get_index_type
|
|
7186
|
+
|
|
7187
|
+
if s.dtype == idx_type
|
|
7188
|
+
return s
|
|
7189
|
+
end
|
|
7190
|
+
|
|
7191
|
+
if !s.dtype.integer?
|
|
7192
|
+
if s.dtype == Boolean
|
|
7193
|
+
_raise_on_boolean_mask
|
|
7194
|
+
else
|
|
7195
|
+
msg = "cannot treat Series of type #{s.dtype} as indices"
|
|
7196
|
+
raise TypeError, msg
|
|
7197
|
+
end
|
|
7198
|
+
end
|
|
7199
|
+
|
|
7200
|
+
if s.len == 0
|
|
7201
|
+
return Series.new(s.name, [], dtype: idx_type)
|
|
7202
|
+
end
|
|
7203
|
+
|
|
7204
|
+
if idx_type == UInt32
|
|
7205
|
+
if [Int64, UInt64].include?(s.dtype) && s.max >= Utils::U32_MAX
|
|
7206
|
+
msg = "index positions should be smaller than 2^32"
|
|
7207
|
+
raise ArgumentError, msg
|
|
7208
|
+
end
|
|
7209
|
+
if s.dtype == Int64 && s.min < -Utils::U32_MAX
|
|
7210
|
+
msg = "index positions should be greater than or equal to -2^32"
|
|
7211
|
+
raise ArgumentError, msg
|
|
7212
|
+
end
|
|
7213
|
+
end
|
|
7214
|
+
|
|
7215
|
+
if s.dtype.signed_integer?
|
|
7216
|
+
if s.min < 0
|
|
7217
|
+
if idx_type == UInt32
|
|
7218
|
+
idxs = [Int8, Int16].include?(s.dtype) ? s.cast(Int32) : s
|
|
7219
|
+
else
|
|
7220
|
+
idxs = [Int8, Int16, Int32].include?(s.dtype) ? s.cast(Int64) : s
|
|
7221
|
+
end
|
|
7222
|
+
|
|
7223
|
+
# Update negative indexes to absolute indexes.
|
|
7224
|
+
return (
|
|
7225
|
+
idxs.to_frame
|
|
7226
|
+
.select(
|
|
7227
|
+
F.when(F.col(idxs.name) < 0)
|
|
7228
|
+
.then(size + F.col(idxs.name))
|
|
7229
|
+
.otherwise(F.col(idxs.name))
|
|
7230
|
+
.cast(idx_type)
|
|
7231
|
+
)
|
|
7232
|
+
.to_series(0)
|
|
7233
|
+
)
|
|
7234
|
+
end
|
|
7235
|
+
end
|
|
7236
|
+
|
|
7237
|
+
s.cast(idx_type)
|
|
7238
|
+
end
|
|
7239
|
+
|
|
7240
|
+
def _raise_on_boolean_mask
|
|
7241
|
+
msg = (
|
|
7242
|
+
"selecting rows by passing a boolean mask to `[]` is not supported" +
|
|
7243
|
+
"\n\nHint: Use the `filter` method instead."
|
|
7244
|
+
)
|
|
7245
|
+
raise TypeError, msg
|
|
7246
|
+
end
|
|
5413
7247
|
end
|
|
5414
7248
|
end
|