Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pola-rs
GitHub Repository: pola-rs/polars
Path: blob/main/crates/polars-ops/src/series/ops/is_in.rs
8483 views
1
use std::hash::Hash;
2
3
use arrow::array::BooleanArray;
4
use arrow::bitmap::BitmapBuilder;
5
use polars_core::prelude::arity::{unary_elementwise, unary_elementwise_values};
6
use polars_core::prelude::*;
7
use polars_core::{with_match_categorical_physical_type, with_match_physical_numeric_polars_type};
8
use polars_utils::total_ord::{ToTotalOrd, TotalEq, TotalHash};
9
10
use self::row_encode::_get_rows_encoded_ca_unordered;
11
12
fn is_in_helper_ca<'a, T>(
13
ca: &'a ChunkedArray<T>,
14
other: &'a ChunkedArray<T>,
15
nulls_equal: bool,
16
) -> PolarsResult<BooleanChunked>
17
where
18
T: PolarsDataType,
19
T::Physical<'a>: TotalHash + TotalEq + ToTotalOrd + Copy,
20
<T::Physical<'a> as ToTotalOrd>::TotalOrdItem: Hash + Eq + Copy,
21
{
22
let mut set = PlHashSet::with_capacity(other.len());
23
other.downcast_iter().for_each(|iter| {
24
iter.iter().for_each(|opt_val| {
25
if let Some(v) = opt_val {
26
set.insert(v.to_total_ord());
27
}
28
})
29
});
30
31
if nulls_equal {
32
if other.has_nulls() {
33
// If the rhs has nulls, then nulls in the left set evaluates to true.
34
Ok(unary_elementwise(ca, |val| {
35
val.is_none_or(|v| set.contains(&v.to_total_ord()))
36
}))
37
} else {
38
// The rhs has no nulls; nulls in the left evaluates to false.
39
Ok(unary_elementwise(ca, |val| {
40
val.is_some_and(|v| set.contains(&v.to_total_ord()))
41
}))
42
}
43
} else {
44
Ok(
45
unary_elementwise_values(ca, |v| set.contains(&v.to_total_ord()))
46
.with_name(ca.name().clone()),
47
)
48
}
49
}
50
51
fn is_in_helper_list_ca<'a, T>(
52
ca_in: &'a ChunkedArray<T>,
53
other: &'a ListChunked,
54
nulls_equal: bool,
55
) -> PolarsResult<BooleanChunked>
56
where
57
T: PolarsPhysicalType,
58
for<'b> T::Physical<'b>: TotalHash + TotalEq + ToTotalOrd + Copy,
59
for<'b> <T::Physical<'b> as ToTotalOrd>::TotalOrdItem: Hash + Eq + Copy,
60
{
61
let offsets = other.offsets()?;
62
let inner = other.get_inner();
63
let inner: &ChunkedArray<T> = inner.as_ref().as_ref();
64
let validity = other.rechunk_validity();
65
66
let mut ca: BooleanChunked = if ca_in.len() == 1 && other.len() != 1 {
67
let value = ca_in.get(0);
68
69
match value {
70
None if !nulls_equal => BooleanChunked::full_null(PlSmallStr::EMPTY, other.len()),
71
value => {
72
let mut builder = BitmapBuilder::with_capacity(other.len());
73
74
for (start, length) in offsets.offset_and_length_iter() {
75
let mut is_in = false;
76
for i in 0..length {
77
is_in |= value.to_total_ord() == inner.get(start + i).to_total_ord();
78
}
79
builder.push(is_in);
80
}
81
82
let values = builder.freeze();
83
84
let result = BooleanArray::new(ArrowDataType::Boolean, values, validity);
85
BooleanChunked::from_chunk_iter(PlSmallStr::EMPTY, [result])
86
},
87
}
88
} else {
89
assert_eq!(ca_in.len(), offsets.len_proxy());
90
{
91
if nulls_equal {
92
let mut builder = BitmapBuilder::with_capacity(ca_in.len());
93
94
for (value, (start, length)) in ca_in.iter().zip(offsets.offset_and_length_iter()) {
95
let mut is_in = false;
96
for i in 0..length {
97
is_in |= value.to_total_ord() == inner.get(start + i).to_total_ord();
98
}
99
builder.push(is_in);
100
}
101
102
let values = builder.freeze();
103
104
let result = BooleanArray::new(ArrowDataType::Boolean, values, validity);
105
BooleanChunked::from_chunk_iter(PlSmallStr::EMPTY, [result])
106
} else {
107
let mut builder = BitmapBuilder::with_capacity(ca_in.len());
108
109
for (value, (start, length)) in ca_in.iter().zip(offsets.offset_and_length_iter()) {
110
let mut is_in = false;
111
if value.is_some() {
112
for i in 0..length {
113
is_in |= value.to_total_ord() == inner.get(start + i).to_total_ord();
114
}
115
}
116
builder.push(is_in);
117
}
118
119
let values = builder.freeze();
120
121
let validity = match (validity, ca_in.rechunk_validity()) {
122
(None, None) => None,
123
(Some(v), None) | (None, Some(v)) => Some(v),
124
(Some(l), Some(r)) => Some(arrow::bitmap::and(&l, &r)),
125
};
126
127
let result = BooleanArray::new(ArrowDataType::Boolean, values, validity);
128
BooleanChunked::from_chunk_iter(PlSmallStr::EMPTY, [result])
129
}
130
}
131
};
132
ca.rename(ca_in.name().clone());
133
Ok(ca)
134
}
135
136
#[cfg(feature = "dtype-array")]
137
fn is_in_helper_array_ca<'a, T>(
138
ca_in: &'a ChunkedArray<T>,
139
other: &'a ArrayChunked,
140
nulls_equal: bool,
141
) -> PolarsResult<BooleanChunked>
142
where
143
T: PolarsPhysicalType,
144
for<'b> T::Physical<'b>: TotalHash + TotalEq + ToTotalOrd + Copy,
145
for<'b> <T::Physical<'b> as ToTotalOrd>::TotalOrdItem: Hash + Eq + Copy,
146
{
147
let width = other.width();
148
let inner = other.get_inner();
149
let inner: &ChunkedArray<T> = inner.as_ref().as_ref();
150
let validity = other.rechunk_validity();
151
152
let mut ca: BooleanChunked = if ca_in.len() == 1 && other.len() != 1 {
153
let value = ca_in.get(0);
154
155
match value {
156
None if !nulls_equal => BooleanChunked::full_null(PlSmallStr::EMPTY, other.len()),
157
value => {
158
let mut builder = BitmapBuilder::with_capacity(other.len());
159
160
for i in 0..other.len() {
161
let mut is_in = false;
162
for j in 0..width {
163
is_in |= value.to_total_ord() == inner.get(i * width + j).to_total_ord();
164
}
165
builder.push(is_in);
166
}
167
168
let values = builder.freeze();
169
170
let result = BooleanArray::new(ArrowDataType::Boolean, values, validity);
171
BooleanChunked::from_chunk_iter(PlSmallStr::EMPTY, [result])
172
},
173
}
174
} else {
175
assert_eq!(ca_in.len(), other.len());
176
{
177
if nulls_equal {
178
let mut builder = BitmapBuilder::with_capacity(ca_in.len());
179
180
for (i, value) in ca_in.iter().enumerate() {
181
let mut is_in = false;
182
for j in 0..width {
183
is_in |= value.to_total_ord() == inner.get(i * width + j).to_total_ord();
184
}
185
builder.push(is_in);
186
}
187
188
let values = builder.freeze();
189
190
let result = BooleanArray::new(ArrowDataType::Boolean, values, validity);
191
BooleanChunked::from_chunk_iter(PlSmallStr::EMPTY, [result])
192
} else {
193
let mut builder = BitmapBuilder::with_capacity(ca_in.len());
194
195
for (i, value) in ca_in.iter().enumerate() {
196
let mut is_in = false;
197
if value.is_some() {
198
for j in 0..width {
199
is_in |=
200
value.to_total_ord() == inner.get(i * width + j).to_total_ord();
201
}
202
}
203
builder.push(is_in);
204
}
205
206
let values = builder.freeze();
207
208
let validity = match (validity, ca_in.rechunk_validity()) {
209
(None, None) => None,
210
(Some(v), None) | (None, Some(v)) => Some(v),
211
(Some(l), Some(r)) => Some(arrow::bitmap::and(&l, &r)),
212
};
213
214
let result = BooleanArray::new(ArrowDataType::Boolean, values, validity);
215
BooleanChunked::from_chunk_iter(PlSmallStr::EMPTY, [result])
216
}
217
}
218
};
219
ca.rename(ca_in.name().clone());
220
Ok(ca)
221
}
222
223
fn is_in_numeric<T>(
224
ca_in: &ChunkedArray<T>,
225
other: &Series,
226
nulls_equal: bool,
227
) -> PolarsResult<BooleanChunked>
228
where
229
T: PolarsNumericType,
230
T::Native: TotalHash + TotalEq + ToTotalOrd,
231
<T::Native as ToTotalOrd>::TotalOrdItem: Hash + Eq + Copy,
232
{
233
match other.dtype() {
234
DataType::List(..) => {
235
let other = other.list()?;
236
if other.len() == 1 {
237
if other.has_nulls() {
238
return Ok(BooleanChunked::full_null(ca_in.name().clone(), ca_in.len()));
239
}
240
241
let other = other.explode(ExplodeOptions {
242
empty_as_null: false,
243
keep_nulls: true,
244
})?;
245
let other = other.as_ref().as_ref();
246
is_in_helper_ca(ca_in, other, nulls_equal)
247
} else {
248
is_in_helper_list_ca(ca_in, other, nulls_equal)
249
}
250
},
251
#[cfg(feature = "dtype-array")]
252
DataType::Array(..) => {
253
let other = other.array()?;
254
if other.len() == 1 {
255
if other.has_nulls() {
256
return Ok(BooleanChunked::full_null(ca_in.name().clone(), ca_in.len()));
257
}
258
259
let other = other.explode(ExplodeOptions {
260
empty_as_null: false,
261
keep_nulls: true,
262
})?;
263
let other = other.as_ref().as_ref();
264
is_in_helper_ca(ca_in, other, nulls_equal)
265
} else {
266
is_in_helper_array_ca(ca_in, other, nulls_equal)
267
}
268
},
269
_ => polars_bail!(opq = is_in, ca_in.dtype(), other.dtype()),
270
}
271
}
272
273
fn is_in_string(
274
ca_in: &StringChunked,
275
other: &Series,
276
nulls_equal: bool,
277
) -> PolarsResult<BooleanChunked> {
278
let other = match other.dtype() {
279
DataType::List(dt) if dt.is_string() || dt.is_enum() || dt.is_categorical() => {
280
let other = other.list()?;
281
other
282
.apply_to_inner(&|mut s| {
283
if dt.is_enum() || dt.is_categorical() {
284
s = s.cast(&DataType::String)?;
285
}
286
let s = s.str()?;
287
Ok(s.as_binary().into_series())
288
})?
289
.into_series()
290
},
291
#[cfg(feature = "dtype-array")]
292
DataType::Array(dt, _) if dt.is_string() || dt.is_enum() || dt.is_categorical() => {
293
let other = other.array()?;
294
other
295
.apply_to_inner(&|mut s| {
296
if dt.is_enum() || dt.is_categorical() {
297
s = s.cast(&DataType::String)?;
298
}
299
Ok(s.str()?.as_binary().into_series())
300
})?
301
.into_series()
302
},
303
_ => polars_bail!(opq = is_in, ca_in.dtype(), other.dtype()),
304
};
305
is_in_binary(&ca_in.as_binary(), &other, nulls_equal)
306
}
307
308
fn is_in_binary(
309
ca_in: &BinaryChunked,
310
other: &Series,
311
nulls_equal: bool,
312
) -> PolarsResult<BooleanChunked> {
313
match other.dtype() {
314
DataType::List(dt) if DataType::Binary == **dt => {
315
let other = other.list()?;
316
if other.len() == 1 {
317
if other.has_nulls() {
318
return Ok(BooleanChunked::full_null(ca_in.name().clone(), ca_in.len()));
319
}
320
321
let other = other.explode(ExplodeOptions {
322
empty_as_null: false,
323
keep_nulls: true,
324
})?;
325
let other = other.binary()?;
326
is_in_helper_ca(ca_in, other, nulls_equal)
327
} else {
328
is_in_helper_list_ca(ca_in, other, nulls_equal)
329
}
330
},
331
#[cfg(feature = "dtype-array")]
332
DataType::Array(dt, _) if DataType::Binary == **dt => {
333
let other = other.array()?;
334
if other.len() == 1 {
335
if other.has_nulls() {
336
return Ok(BooleanChunked::full_null(ca_in.name().clone(), ca_in.len()));
337
}
338
339
let other = other.explode(ExplodeOptions {
340
empty_as_null: false,
341
keep_nulls: true,
342
})?;
343
let other = other.binary()?;
344
is_in_helper_ca(ca_in, other, nulls_equal)
345
} else {
346
is_in_helper_array_ca(ca_in, other, nulls_equal)
347
}
348
},
349
_ => polars_bail!(opq = is_in, ca_in.dtype(), other.dtype()),
350
}
351
}
352
353
fn is_in_boolean(
354
ca_in: &BooleanChunked,
355
other: &Series,
356
nulls_equal: bool,
357
) -> PolarsResult<BooleanChunked> {
358
fn is_in_boolean_broadcast(
359
ca_in: &BooleanChunked,
360
other: &BooleanChunked,
361
nulls_equal: bool,
362
) -> PolarsResult<BooleanChunked> {
363
let has_true = other.any();
364
let nc = other.null_count();
365
366
let has_false = if nc == 0 {
367
!other.all()
368
} else {
369
(other.sum().unwrap() as usize + nc) != other.len()
370
};
371
let value_map = |v| if v { has_true } else { has_false };
372
if nulls_equal {
373
if other.has_nulls() {
374
// If the rhs has nulls, then nulls in the left set evaluates to true.
375
Ok(ca_in.apply(|opt_v| Some(opt_v.is_none_or(value_map))))
376
} else {
377
// The rhs has no nulls; nulls in the left evaluates to false.
378
Ok(ca_in.apply(|opt_v| Some(opt_v.is_some_and(value_map))))
379
}
380
} else {
381
Ok(ca_in
382
.apply_values(value_map)
383
.with_name(ca_in.name().clone()))
384
}
385
}
386
387
match other.dtype() {
388
DataType::List(dt) if ca_in.dtype() == &**dt => {
389
let other = other.list()?;
390
if other.len() == 1 {
391
if other.has_nulls() {
392
return Ok(BooleanChunked::full_null(ca_in.name().clone(), ca_in.len()));
393
}
394
395
let other = other.explode(ExplodeOptions {
396
empty_as_null: false,
397
keep_nulls: true,
398
})?;
399
let other = other.bool()?;
400
is_in_boolean_broadcast(ca_in, other, nulls_equal)
401
} else {
402
is_in_helper_list_ca(ca_in, other, nulls_equal)
403
}
404
},
405
#[cfg(feature = "dtype-array")]
406
DataType::Array(dt, _) if ca_in.dtype() == &**dt => {
407
let other = other.array()?;
408
if other.len() == 1 {
409
if other.has_nulls() {
410
return Ok(BooleanChunked::full_null(ca_in.name().clone(), ca_in.len()));
411
}
412
413
let other = other.explode(ExplodeOptions {
414
empty_as_null: false,
415
keep_nulls: true,
416
})?;
417
let other = other.bool()?;
418
is_in_boolean_broadcast(ca_in, other, nulls_equal)
419
} else {
420
is_in_helper_array_ca(ca_in, other, nulls_equal)
421
}
422
},
423
_ => polars_bail!(opq = is_in, ca_in.dtype(), other.dtype()),
424
}
425
}
426
427
#[cfg(feature = "dtype-categorical")]
428
fn is_in_cat_and_enum<T: PolarsCategoricalType>(
429
ca_in: &CategoricalChunked<T>,
430
other: &Series,
431
nulls_equal: bool,
432
) -> PolarsResult<BooleanChunked>
433
where
434
T::Native: ToTotalOrd<TotalOrdItem = T::Native>,
435
{
436
let to_categories = match (ca_in.dtype(), other.dtype().inner_dtype().unwrap()) {
437
(DataType::Enum(_, mapping) | DataType::Categorical(_, mapping), DataType::String) => {
438
(&|s: Series| {
439
let ca = s.str()?;
440
let ca: ChunkedArray<T::PolarsPhysical> = ca
441
.iter()
442
.flat_map(|opt_s| {
443
if let Some(s) = opt_s {
444
Some(mapping.get_cat(s).map(T::Native::from_cat))
445
} else {
446
Some(None)
447
}
448
})
449
.collect_ca(PlSmallStr::EMPTY);
450
Ok(ca.into_series())
451
}) as _
452
},
453
(DataType::Categorical(lcats, _), DataType::Categorical(rcats, _)) => {
454
ensure_same_categories(lcats, rcats)?;
455
(&|s: Series| Ok(s.cat::<T>()?.physical().clone().into_series())) as _
456
},
457
(DataType::Enum(lfcats, _), DataType::Enum(rfcats, _)) => {
458
ensure_same_frozen_categories(lfcats, rfcats)?;
459
(&|s: Series| Ok(s.cat::<T>()?.physical().clone().into_series())) as _
460
},
461
_ => polars_bail!(opq = is_in, ca_in.dtype(), other.dtype()),
462
};
463
464
let other = match other.dtype() {
465
DataType::List(_) => other.list()?.apply_to_inner(to_categories)?.into_series(),
466
#[cfg(feature = "dtype-array")]
467
DataType::Array(_, _) => other.array()?.apply_to_inner(to_categories)?.into_series(),
468
_ => polars_bail!(opq = is_in, ca_in.dtype(), other.dtype()),
469
};
470
471
is_in_numeric(ca_in.physical(), &other, nulls_equal)
472
}
473
474
fn is_in_null(s: &Series, other: &Series, nulls_equal: bool) -> PolarsResult<BooleanChunked> {
475
if nulls_equal {
476
let ca_in = s.null()?;
477
Ok(match other.dtype() {
478
DataType::List(_) => {
479
let other = other.list()?;
480
if other.len() == 1 {
481
if other.has_nulls() {
482
return Ok(BooleanChunked::full_null(ca_in.name().clone(), ca_in.len()));
483
}
484
485
let other = other.explode(ExplodeOptions {
486
empty_as_null: false,
487
keep_nulls: true,
488
})?;
489
BooleanChunked::from_iter_values(
490
ca_in.name().clone(),
491
std::iter::repeat_n(other.has_nulls(), ca_in.len()),
492
)
493
} else {
494
other.apply_amortized_generic(|opt_s| {
495
Some(opt_s.map(|s| s.as_ref().has_nulls()) == Some(true))
496
})
497
}
498
},
499
#[cfg(feature = "dtype-array")]
500
DataType::Array(_, _) => {
501
let other = other.array()?;
502
if other.len() == 1 {
503
if other.has_nulls() {
504
return Ok(BooleanChunked::full_null(ca_in.name().clone(), ca_in.len()));
505
}
506
507
let other = other.explode(ExplodeOptions {
508
empty_as_null: false,
509
keep_nulls: true,
510
})?;
511
BooleanChunked::from_iter_values(
512
ca_in.name().clone(),
513
std::iter::repeat_n(other.has_nulls(), ca_in.len()),
514
)
515
} else {
516
other.apply_amortized_generic(|opt_s| {
517
Some(opt_s.map(|s| s.as_ref().has_nulls()) == Some(true))
518
})
519
}
520
},
521
_ => polars_bail!(opq = is_in, ca_in.dtype(), other.dtype()),
522
})
523
} else {
524
let out = s.cast(&DataType::Boolean)?;
525
let ca_bool = out.bool()?.clone();
526
Ok(ca_bool)
527
}
528
}
529
530
#[cfg(feature = "dtype-decimal")]
531
fn is_in_decimal(
532
ca_in: &DecimalChunked,
533
other: &Series,
534
nulls_equal: bool,
535
) -> PolarsResult<BooleanChunked> {
536
let Some(DataType::Decimal(other_precision, other_scale)) = other.dtype().inner_dtype() else {
537
polars_bail!(opq = is_in, ca_in.dtype(), other.dtype());
538
};
539
let prec = ca_in.precision().max(*other_precision);
540
let scale = ca_in.scale().max(*other_scale);
541
542
// We convert both sides to a common scale, mapping any out-of-range values to unique integers,
543
// allowing us to then use is_in on the integer representation.
544
let sentinel_in = i128::MAX;
545
let sentinel_other = i128::MAX - 1;
546
let ca_in_phys = ca_in.into_phys_with_prec_scale_or_sentinel(prec, scale, sentinel_in);
547
548
match other.dtype() {
549
DataType::List(_) => {
550
let other = other.list()?;
551
let other = other.apply_to_inner(&|s| {
552
let s = s.decimal()?;
553
let s = s.into_phys_with_prec_scale_or_sentinel(prec, scale, sentinel_other);
554
Ok(s.to_owned().into_series())
555
})?;
556
let other = other.into_series();
557
is_in_numeric(&ca_in_phys, &other, nulls_equal)
558
},
559
#[cfg(feature = "dtype-array")]
560
DataType::Array(_, _) => {
561
let other = other.array()?;
562
let other = other.apply_to_inner(&|s| {
563
let s = s.decimal()?;
564
let s = s.into_phys_with_prec_scale_or_sentinel(prec, scale, sentinel_other);
565
Ok(s.to_owned().into_series())
566
})?;
567
let other = other.into_series();
568
is_in_numeric(&ca_in_phys, &other, nulls_equal)
569
},
570
_ => unreachable!(),
571
}
572
}
573
574
fn is_in_row_encoded(
575
s: &Series,
576
other: &Series,
577
nulls_equal: bool,
578
) -> PolarsResult<BooleanChunked> {
579
let ca_in = _get_rows_encoded_ca_unordered(s.name().clone(), &[s.clone().into_column()])?;
580
let mut mask = match other.dtype() {
581
DataType::List(_) => {
582
let other = other.list()?;
583
let other = other.apply_to_inner(&|s| {
584
Ok(
585
_get_rows_encoded_ca_unordered(s.name().clone(), &[s.into_column()])?
586
.into_series(),
587
)
588
})?;
589
if other.len() == 1 {
590
if other.has_nulls() {
591
return Ok(BooleanChunked::full_null(ca_in.name().clone(), ca_in.len()));
592
}
593
594
let other = other.explode(ExplodeOptions {
595
empty_as_null: false,
596
keep_nulls: true,
597
})?;
598
let other = other.binary_offset()?;
599
is_in_helper_ca(&ca_in, other, nulls_equal)
600
} else {
601
is_in_helper_list_ca(&ca_in, &other, nulls_equal)
602
}
603
},
604
#[cfg(feature = "dtype-array")]
605
DataType::Array(_, _) => {
606
let other = other.array()?;
607
let other = other.apply_to_inner(&|s| {
608
Ok(
609
_get_rows_encoded_ca_unordered(s.name().clone(), &[s.into_column()])?
610
.into_series(),
611
)
612
})?;
613
if other.len() == 1 {
614
if other.has_nulls() {
615
return Ok(BooleanChunked::full_null(ca_in.name().clone(), ca_in.len()));
616
}
617
618
let other = other.explode(ExplodeOptions {
619
empty_as_null: false,
620
keep_nulls: true,
621
})?;
622
let other = other.binary_offset()?;
623
is_in_helper_ca(&ca_in, other, nulls_equal)
624
} else {
625
is_in_helper_array_ca(&ca_in, &other, nulls_equal)
626
}
627
},
628
_ => unreachable!(),
629
}?;
630
631
let mut validity = other.rechunk_validity();
632
if !nulls_equal {
633
validity = match (validity, s.rechunk_validity()) {
634
(None, None) => None,
635
(Some(v), None) | (None, Some(v)) => Some(v),
636
(Some(l), Some(r)) => Some(arrow::bitmap::and(&l, &r)),
637
};
638
}
639
640
assert_eq!(mask.null_count(), 0);
641
mask.with_validities(&[validity]);
642
643
Ok(mask)
644
}
645
646
pub fn is_in(s: &Series, other: &Series, nulls_equal: bool) -> PolarsResult<BooleanChunked> {
647
polars_ensure!(
648
s.len() == other.len() || s.len() == 1 || other.len() == 1,
649
length_mismatch = "is_in",
650
s.len(),
651
other.len()
652
);
653
654
#[allow(unused_mut)]
655
let mut other_is_valid_type = matches!(other.dtype(), DataType::List(_));
656
#[cfg(feature = "dtype-array")]
657
{
658
other_is_valid_type |= matches!(other.dtype(), DataType::Array(..))
659
}
660
polars_ensure!(other_is_valid_type, opq = is_in, s.dtype(), other.dtype());
661
662
match s.dtype() {
663
#[cfg(feature = "dtype-categorical")]
664
dt @ DataType::Categorical(_, _) | dt @ DataType::Enum(_, _) => {
665
with_match_categorical_physical_type!(dt.cat_physical().unwrap(), |$C| {
666
is_in_cat_and_enum(s.cat::<$C>().unwrap(), other, nulls_equal)
667
})
668
},
669
DataType::String => {
670
let ca = s.str().unwrap();
671
is_in_string(ca, other, nulls_equal)
672
},
673
DataType::Binary => {
674
let ca = s.binary().unwrap();
675
is_in_binary(ca, other, nulls_equal)
676
},
677
DataType::Boolean => {
678
let ca = s.bool().unwrap();
679
is_in_boolean(ca, other, nulls_equal)
680
},
681
DataType::Null => is_in_null(s, other, nulls_equal),
682
#[cfg(feature = "dtype-decimal")]
683
DataType::Decimal(_, _) => {
684
let ca_in = s.decimal()?;
685
is_in_decimal(ca_in, other, nulls_equal)
686
},
687
dt if dt.is_nested() => is_in_row_encoded(s, other, nulls_equal),
688
dt if dt.to_physical().is_primitive_numeric() => {
689
let s = s.to_physical_repr();
690
let other = other.to_physical_repr();
691
let other = other.as_ref();
692
with_match_physical_numeric_polars_type!(s.dtype(), |$T| {
693
let ca: &ChunkedArray<$T> = s.as_ref().as_ref().as_ref();
694
is_in_numeric(ca, other, nulls_equal)
695
})
696
},
697
dt => polars_bail!(opq = is_in, dt),
698
}
699
}
700
701