Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pola-rs
GitHub Repository: pola-rs/polars
Path: blob/main/py-polars/tests/unit/interchange/test_from_dataframe.py
8415 views
1
from __future__ import annotations
2
3
from datetime import date, datetime, time, timedelta
4
from typing import TYPE_CHECKING, Any
5
6
import pandas as pd
7
import pyarrow as pa
8
import pytest
9
10
import polars as pl
11
from polars.interchange.buffer import PolarsBuffer
12
from polars.interchange.column import PolarsColumn
13
from polars.interchange.from_dataframe import (
14
_categorical_column_to_series,
15
_column_to_series,
16
_construct_data_buffer,
17
_construct_offsets_buffer,
18
_construct_validity_buffer,
19
_construct_validity_buffer_from_bitmask,
20
_construct_validity_buffer_from_bytemask,
21
_string_column_to_series,
22
)
23
from polars.interchange.protocol import (
24
ColumnNullType,
25
CopyNotAllowedError,
26
DtypeKind,
27
Endianness,
28
)
29
from polars.testing import assert_frame_equal, assert_series_equal
30
31
NE = Endianness.NATIVE
32
33
if TYPE_CHECKING:
34
from tests.conftest import PlMonkeyPatch
35
36
37
def test_from_dataframe_polars() -> None:
38
df = pl.DataFrame({"a": [1, 2], "b": [3.0, 4.0], "c": ["foo", "bar"]})
39
with pytest.deprecated_call(match="`allow_copy` is deprecated"):
40
result = pl.from_dataframe(df, allow_copy=False)
41
assert_frame_equal(result, df)
42
43
44
def test_from_dataframe_polars_interchange_fast_path() -> None:
45
df = pl.DataFrame(
46
{"a": [1, 2], "b": [3.0, 4.0], "c": ["foo", "bar"]},
47
schema_overrides={"c": pl.Categorical},
48
)
49
dfi = df.__dataframe__()
50
with pytest.deprecated_call(match="`allow_copy` is deprecated"):
51
result = pl.from_dataframe(dfi, allow_copy=False)
52
assert_frame_equal(result, df)
53
54
55
def test_from_dataframe_categorical() -> None:
56
df = pl.DataFrame({"a": ["foo", "bar"]}, schema={"a": pl.Categorical})
57
df_pa = df.to_arrow()
58
59
with pytest.deprecated_call(match="`allow_copy` is deprecated"):
60
result = pl.from_dataframe(df_pa, allow_copy=True)
61
expected = pl.DataFrame({"a": ["foo", "bar"]}, schema={"a": pl.Categorical})
62
assert_frame_equal(result, expected)
63
64
65
def test_from_dataframe_empty_string_zero_copy() -> None:
66
df = pl.DataFrame({"a": []}, schema={"a": pl.String})
67
df_pa = df.to_arrow()
68
with pytest.deprecated_call(match="`allow_copy` is deprecated"):
69
result = pl.from_dataframe(df_pa, allow_copy=False)
70
assert_frame_equal(result, df)
71
72
73
def test_from_dataframe_empty_bool_zero_copy() -> None:
74
df = pl.DataFrame(schema={"a": pl.Boolean})
75
df_pd = df.to_pandas()
76
with pytest.deprecated_call(match="`allow_copy` is deprecated"):
77
result = pl.from_dataframe(df_pd, allow_copy=False)
78
assert_frame_equal(result, df)
79
80
81
def test_from_dataframe_empty_categories_zero_copy() -> None:
82
df = pl.DataFrame(schema={"a": pl.Enum([])})
83
df_pa = df.to_arrow()
84
with pytest.deprecated_call(match="`allow_copy` is deprecated"):
85
result = pl.from_dataframe(df_pa, allow_copy=False)
86
assert_frame_equal(result, df)
87
88
89
def test_from_dataframe_pandas_zero_copy() -> None:
90
data = {"a": [1, 2], "b": [3.0, 4.0]}
91
92
df = pd.DataFrame(data)
93
with pytest.deprecated_call(match="`allow_copy` is deprecated"):
94
result = pl.from_dataframe(df, allow_copy=False)
95
expected = pl.DataFrame(data)
96
assert_frame_equal(result, expected)
97
98
99
def test_from_dataframe_pyarrow_table_zero_copy() -> None:
100
df = pl.DataFrame(
101
{
102
"a": [1, 2],
103
"b": [3.0, 4.0],
104
}
105
)
106
df_pa = df.to_arrow()
107
108
with pytest.deprecated_call(match="`allow_copy` is deprecated"):
109
result = pl.from_dataframe(df_pa, allow_copy=False)
110
assert_frame_equal(result, df)
111
112
113
def test_from_dataframe_pyarrow_empty_table() -> None:
114
df = pl.Series("a", dtype=pl.Int8).to_frame()
115
df_pa = df.to_arrow()
116
117
with pytest.deprecated_call(match="`allow_copy` is deprecated"):
118
result = pl.from_dataframe(df_pa, allow_copy=False)
119
assert_frame_equal(result, df)
120
121
122
def test_from_dataframe_pyarrow_recordbatch_zero_copy() -> None:
123
a = pa.array([1, 2])
124
b = pa.array([3.0, 4.0])
125
126
batch = pa.record_batch([a, b], names=["a", "b"])
127
with pytest.deprecated_call(match="`allow_copy` is deprecated"):
128
result = pl.from_dataframe(batch, allow_copy=False)
129
130
expected = pl.DataFrame({"a": [1, 2], "b": [3.0, 4.0]})
131
assert_frame_equal(result, expected)
132
133
134
def test_from_dataframe_invalid_type() -> None:
135
df = [[1, 2], [3, 4]]
136
with pytest.raises(TypeError):
137
pl.from_dataframe(df) # type: ignore[arg-type]
138
139
140
def test_from_dataframe_pyarrow_boolean() -> None:
141
df = pl.Series("a", [True, False]).to_frame()
142
df_pa = df.to_arrow()
143
144
result = pl.from_dataframe(df_pa)
145
assert_frame_equal(result, df)
146
147
with pytest.deprecated_call(match="`allow_copy` is deprecated"):
148
result = pl.from_dataframe(df_pa, allow_copy=False)
149
assert_frame_equal(result, df)
150
151
152
def test_from_dataframe_chunked() -> None:
153
df = pl.Series("a", [0, 1], dtype=pl.Int8).to_frame()
154
df_chunked = pl.concat([df[:1], df[1:]], rechunk=False)
155
156
df_pa = df_chunked.to_arrow()
157
result = pl.from_dataframe(df_pa, rechunk=False)
158
159
assert_frame_equal(result, df_chunked)
160
assert result.n_chunks() == 2
161
162
163
@pytest.mark.may_fail_auto_streaming
164
@pytest.mark.may_fail_cloud # reason: chunking
165
def test_from_dataframe_chunked_string() -> None:
166
df = pl.Series("a", ["a", None, "bc", "d", None, "efg"]).to_frame()
167
df_chunked = pl.concat([df[:1], df[1:3], df[3:]], rechunk=False)
168
169
df_pa = df_chunked.to_arrow()
170
result = pl.from_dataframe(df_pa, rechunk=False)
171
172
assert_frame_equal(result, df_chunked)
173
assert result.n_chunks() == 3
174
175
176
def test_from_dataframe_pandas_nan_as_null() -> None:
177
df = pd.Series([1.0, float("nan"), float("inf")], name="a").to_frame()
178
result = pl.from_dataframe(df)
179
expected = pl.Series("a", [1.0, None, float("inf")]).to_frame()
180
assert_frame_equal(result, expected)
181
assert result.n_chunks() == 1
182
183
184
def test_from_dataframe_pandas_boolean_bytes() -> None:
185
df = pd.Series([True, False], name="a").to_frame()
186
result = pl.from_dataframe(df)
187
188
expected = pl.Series("a", [True, False]).to_frame()
189
assert_frame_equal(result, expected)
190
191
with pytest.deprecated_call(match="`allow_copy` is deprecated"):
192
result = pl.from_dataframe(df, allow_copy=False)
193
expected = pl.Series("a", [True, False]).to_frame()
194
assert_frame_equal(result, expected)
195
196
197
def test_from_dataframe_categorical_pandas() -> None:
198
values = ["a", "b", None, "a"]
199
200
df_pd = pd.Series(values, dtype="category", name="a").to_frame()
201
202
result = pl.from_dataframe(df_pd)
203
expected = pl.Series("a", values, dtype=pl.Categorical).to_frame()
204
assert_frame_equal(result, expected)
205
206
with pytest.deprecated_call(match="`allow_copy` is deprecated"):
207
result = pl.from_dataframe(df_pd, allow_copy=False)
208
expected = pl.Series("a", values, dtype=pl.Categorical).to_frame()
209
assert_frame_equal(result, expected)
210
211
212
def test_from_dataframe_categorical_pyarrow() -> None:
213
values = ["a", "b", None, "a"]
214
215
dtype = pa.dictionary(pa.int32(), pa.utf8())
216
arr = pa.array(values, dtype)
217
df_pa = pa.Table.from_arrays([arr], names=["a"])
218
219
result = pl.from_dataframe(df_pa)
220
expected = pl.Series("a", values, dtype=pl.Categorical).to_frame()
221
assert_frame_equal(result, expected)
222
223
with pytest.deprecated_call(match="`allow_copy` is deprecated"):
224
result = pl.from_dataframe(df_pa, allow_copy=False)
225
assert_frame_equal(result, expected)
226
227
228
def test_from_dataframe_categorical_non_string_keys() -> None:
229
values = [1, 2, None, 1]
230
231
dtype = pa.dictionary(pa.uint32(), pa.int32())
232
arr = pa.array(values, dtype)
233
df_pa = pa.Table.from_arrays([arr], names=["a"])
234
result = pl.from_dataframe(df_pa)
235
expected = pl.DataFrame({"a": [1, 2, None, 1]}, schema={"a": pl.Int32})
236
assert_frame_equal(result, expected)
237
238
239
def test_from_dataframe_categorical_non_u32_values() -> None:
240
values = [None, None]
241
242
dtype = pa.dictionary(pa.int8(), pa.utf8())
243
arr = pa.array(values, dtype)
244
df_pa = pa.Table.from_arrays([arr], names=["a"])
245
246
result = pl.from_dataframe(df_pa)
247
expected = pl.Series("a", values, dtype=pl.Categorical).to_frame()
248
assert_frame_equal(result, expected)
249
250
with pytest.deprecated_call(match="`allow_copy` is deprecated"):
251
result = pl.from_dataframe(df_pa, allow_copy=False)
252
assert_frame_equal(result, expected)
253
254
255
class PatchableColumn(PolarsColumn):
256
"""Helper class that allows patching certain PolarsColumn properties."""
257
258
describe_null: tuple[ColumnNullType, Any] = (ColumnNullType.USE_BITMASK, 0)
259
describe_categorical: dict[str, Any] = {} # type: ignore[assignment] # noqa: RUF012
260
null_count = 0
261
262
263
def test_column_to_series_use_sentinel_i64_min() -> None:
264
I64_MIN = -9223372036854775808
265
dtype = pl.Datetime("us")
266
physical = pl.Series([0, I64_MIN])
267
logical = physical.cast(dtype)
268
269
col = PatchableColumn(logical)
270
col.describe_null = (ColumnNullType.USE_SENTINEL, I64_MIN)
271
col.null_count = 1
272
273
result = _column_to_series(col, dtype, allow_copy=True)
274
expected = pl.Series([datetime(1970, 1, 1), None])
275
assert_series_equal(result, expected)
276
277
278
def test_column_to_series_duration() -> None:
279
s = pl.Series([timedelta(seconds=10), timedelta(days=5), None])
280
col = PolarsColumn(s)
281
result = _column_to_series(col, s.dtype, allow_copy=True)
282
assert_series_equal(result, s)
283
284
285
def test_column_to_series_time() -> None:
286
s = pl.Series([time(10, 0), time(23, 59, 59), None])
287
col = PolarsColumn(s)
288
result = _column_to_series(col, s.dtype, allow_copy=True)
289
assert_series_equal(result, s)
290
291
292
def test_column_to_series_use_sentinel_date() -> None:
293
mask_value = date(1900, 1, 1)
294
295
s = pl.Series([date(1970, 1, 1), mask_value, date(2000, 1, 1)])
296
297
col = PatchableColumn(s)
298
col.describe_null = (ColumnNullType.USE_SENTINEL, mask_value)
299
col.null_count = 1
300
301
result = _column_to_series(col, pl.Date, allow_copy=True)
302
expected = pl.Series([date(1970, 1, 1), None, date(2000, 1, 1)])
303
assert_series_equal(result, expected)
304
305
306
def test_column_to_series_use_sentinel_datetime() -> None:
307
dtype = pl.Datetime("ns")
308
mask_value = datetime(1900, 1, 1)
309
310
s = pl.Series([datetime(1970, 1, 1), mask_value, datetime(2000, 1, 1)], dtype=dtype)
311
312
col = PatchableColumn(s)
313
col.describe_null = (ColumnNullType.USE_SENTINEL, mask_value)
314
col.null_count = 1
315
316
result = _column_to_series(col, dtype, allow_copy=True)
317
expected = pl.Series(
318
[datetime(1970, 1, 1), None, datetime(2000, 1, 1)], dtype=dtype
319
)
320
assert_series_equal(result, expected)
321
322
323
def test_column_to_series_use_sentinel_invalid_value() -> None:
324
dtype = pl.Datetime("ns")
325
mask_value = "invalid"
326
327
s = pl.Series([datetime(1970, 1, 1), None, datetime(2000, 1, 1)], dtype=dtype)
328
329
col = PatchableColumn(s)
330
col.describe_null = (ColumnNullType.USE_SENTINEL, mask_value)
331
col.null_count = 1
332
333
with pytest.raises(
334
TypeError,
335
match=r"invalid sentinel value for column of type Datetime\(time_unit='ns', time_zone=None\): 'invalid'",
336
):
337
_column_to_series(col, dtype, allow_copy=True)
338
339
340
def test_string_column_to_series_no_offsets() -> None:
341
s = pl.Series([97, 98, 99])
342
col = PolarsColumn(s)
343
with pytest.raises(
344
RuntimeError,
345
match="cannot create String column without an offsets buffer",
346
):
347
_string_column_to_series(col, allow_copy=True)
348
349
350
def test_categorical_column_to_series_non_dictionary() -> None:
351
s = pl.Series(["a", "b", None, "a"], dtype=pl.Categorical)
352
353
col = PatchableColumn(s)
354
col.describe_categorical = {"is_dictionary": False}
355
356
with pytest.raises(
357
NotImplementedError, match="non-dictionary categoricals are not yet supported"
358
):
359
_categorical_column_to_series(col, allow_copy=True)
360
361
362
def test_construct_data_buffer() -> None:
363
data = pl.Series([0, 1, 3, 3, 9], dtype=pl.Int64)
364
buffer = PolarsBuffer(data)
365
dtype = (DtypeKind.INT, 64, "l", NE)
366
367
result = _construct_data_buffer(buffer, dtype, length=5, allow_copy=True)
368
assert_series_equal(result, data)
369
370
371
def test_construct_data_buffer_boolean_sliced() -> None:
372
data = pl.Series([False, True, True, False])
373
data_sliced = data[2:]
374
buffer = PolarsBuffer(data_sliced)
375
dtype = (DtypeKind.BOOL, 1, "b", NE)
376
377
result = _construct_data_buffer(buffer, dtype, length=2, offset=2, allow_copy=True)
378
assert_series_equal(result, data_sliced)
379
380
381
def test_construct_data_buffer_logical_dtype() -> None:
382
data = pl.Series([100, 200, 300], dtype=pl.Int32)
383
buffer = PolarsBuffer(data)
384
dtype = (DtypeKind.DATETIME, 32, "tdD", NE)
385
386
result = _construct_data_buffer(buffer, dtype, length=3, allow_copy=True)
387
assert_series_equal(result, data)
388
389
390
def test_construct_offsets_buffer() -> None:
391
data = pl.Series([0, 1, 3, 3, 9], dtype=pl.Int64)
392
buffer = PolarsBuffer(data)
393
dtype = (DtypeKind.INT, 64, "l", NE)
394
395
result = _construct_offsets_buffer(buffer, dtype, offset=0, allow_copy=True)
396
assert_series_equal(result, data)
397
398
399
def test_construct_offsets_buffer_offset() -> None:
400
data = pl.Series([0, 1, 3, 3, 9], dtype=pl.Int64)
401
buffer = PolarsBuffer(data)
402
dtype = (DtypeKind.INT, 64, "l", NE)
403
offset = 2
404
405
result = _construct_offsets_buffer(buffer, dtype, offset=offset, allow_copy=True)
406
assert_series_equal(result, data[offset:])
407
408
409
def test_construct_offsets_buffer_copy() -> None:
410
data = pl.Series([0, 1, 3, 3, 9], dtype=pl.UInt32)
411
buffer = PolarsBuffer(data)
412
dtype = (DtypeKind.UINT, 32, "I", NE)
413
414
with pytest.raises(CopyNotAllowedError):
415
_construct_offsets_buffer(buffer, dtype, offset=0, allow_copy=False)
416
417
result = _construct_offsets_buffer(buffer, dtype, offset=0, allow_copy=True)
418
expected = pl.Series([0, 1, 3, 3, 9], dtype=pl.Int64)
419
assert_series_equal(result, expected)
420
421
422
@pytest.fixture
423
def bitmask() -> PolarsBuffer:
424
data = pl.Series([False, True, True, False])
425
return PolarsBuffer(data)
426
427
428
@pytest.fixture
429
def bytemask() -> PolarsBuffer:
430
data = pl.Series([0, 1, 1, 0], dtype=pl.UInt8)
431
return PolarsBuffer(data)
432
433
434
def test_construct_validity_buffer_non_nullable() -> None:
435
s = pl.Series([1, 2, 3])
436
437
col = PatchableColumn(s)
438
col.describe_null = (ColumnNullType.NON_NULLABLE, None)
439
col.null_count = 1
440
441
result = _construct_validity_buffer(None, col, s.dtype, s, allow_copy=True)
442
assert result is None
443
444
445
def test_construct_validity_buffer_null_count() -> None:
446
s = pl.Series([1, 2, 3])
447
448
col = PatchableColumn(s)
449
col.describe_null = (ColumnNullType.USE_SENTINEL, -1)
450
col.null_count = 0
451
452
result = _construct_validity_buffer(None, col, s.dtype, s, allow_copy=True)
453
assert result is None
454
455
456
def test_construct_validity_buffer_use_bitmask(bitmask: PolarsBuffer) -> None:
457
s = pl.Series([1, 2, 3, 4])
458
459
col = PatchableColumn(s)
460
col.describe_null = (ColumnNullType.USE_BITMASK, 0)
461
col.null_count = 2
462
463
dtype = (DtypeKind.BOOL, 1, "b", NE)
464
validity_buffer_info = (bitmask, dtype)
465
466
result = _construct_validity_buffer(
467
validity_buffer_info, col, s.dtype, s, allow_copy=True
468
)
469
expected = pl.Series([False, True, True, False])
470
assert_series_equal(result, expected) # type: ignore[arg-type]
471
472
result = _construct_validity_buffer(None, col, s.dtype, s, allow_copy=True)
473
assert result is None
474
475
476
def test_construct_validity_buffer_use_bytemask(bytemask: PolarsBuffer) -> None:
477
s = pl.Series([1, 2, 3, 4])
478
479
col = PatchableColumn(s)
480
col.describe_null = (ColumnNullType.USE_BYTEMASK, 0)
481
col.null_count = 2
482
483
dtype = (DtypeKind.UINT, 8, "C", NE)
484
validity_buffer_info = (bytemask, dtype)
485
486
result = _construct_validity_buffer(
487
validity_buffer_info, col, s.dtype, s, allow_copy=True
488
)
489
expected = pl.Series([False, True, True, False])
490
assert_series_equal(result, expected) # type: ignore[arg-type]
491
492
result = _construct_validity_buffer(None, col, s.dtype, s, allow_copy=True)
493
assert result is None
494
495
496
def test_construct_validity_buffer_use_nan() -> None:
497
s = pl.Series([1.0, 2.0, float("nan")])
498
499
col = PatchableColumn(s)
500
col.describe_null = (ColumnNullType.USE_NAN, None)
501
col.null_count = 1
502
503
result = _construct_validity_buffer(None, col, s.dtype, s, allow_copy=True)
504
expected = pl.Series([True, True, False])
505
assert_series_equal(result, expected) # type: ignore[arg-type]
506
507
with pytest.raises(CopyNotAllowedError, match="bitmask must be constructed"):
508
_construct_validity_buffer(None, col, s.dtype, s, allow_copy=False)
509
510
511
def test_construct_validity_buffer_use_sentinel() -> None:
512
s = pl.Series(["a", "bc", "NULL"])
513
514
col = PatchableColumn(s)
515
col.describe_null = (ColumnNullType.USE_SENTINEL, "NULL")
516
col.null_count = 1
517
518
result = _construct_validity_buffer(None, col, s.dtype, s, allow_copy=True)
519
expected = pl.Series([True, True, False])
520
assert_series_equal(result, expected) # type: ignore[arg-type]
521
522
with pytest.raises(CopyNotAllowedError, match="bitmask must be constructed"):
523
_construct_validity_buffer(None, col, s.dtype, s, allow_copy=False)
524
525
526
def test_construct_validity_buffer_unsupported() -> None:
527
s = pl.Series([1, 2, 3])
528
529
col = PatchableColumn(s)
530
col.describe_null = (100, None) # type: ignore[assignment]
531
col.null_count = 1
532
533
with pytest.raises(NotImplementedError, match="unsupported null type: 100"):
534
_construct_validity_buffer(None, col, s.dtype, s, allow_copy=True)
535
536
537
@pytest.mark.parametrize("allow_copy", [True, False])
538
def test_construct_validity_buffer_from_bitmask(
539
allow_copy: bool, bitmask: PolarsBuffer
540
) -> None:
541
result = _construct_validity_buffer_from_bitmask(
542
bitmask, null_value=0, offset=0, length=4, allow_copy=allow_copy
543
)
544
expected = pl.Series([False, True, True, False])
545
assert_series_equal(result, expected)
546
547
548
def test_construct_validity_buffer_from_bitmask_inverted(bitmask: PolarsBuffer) -> None:
549
result = _construct_validity_buffer_from_bitmask(
550
bitmask, null_value=1, offset=0, length=4, allow_copy=True
551
)
552
expected = pl.Series([True, False, False, True])
553
assert_series_equal(result, expected)
554
555
556
def test_construct_validity_buffer_from_bitmask_zero_copy_fails(
557
bitmask: PolarsBuffer,
558
) -> None:
559
with pytest.raises(CopyNotAllowedError):
560
_construct_validity_buffer_from_bitmask(
561
bitmask, null_value=1, offset=0, length=4, allow_copy=False
562
)
563
564
565
def test_construct_validity_buffer_from_bitmask_sliced() -> None:
566
data = pl.Series([False, True, True, False])
567
data_sliced = data[2:]
568
bitmask = PolarsBuffer(data_sliced)
569
570
result = _construct_validity_buffer_from_bitmask(
571
bitmask, null_value=0, offset=2, length=2, allow_copy=True
572
)
573
assert_series_equal(result, data_sliced)
574
575
576
def test_construct_validity_buffer_from_bytemask(bytemask: PolarsBuffer) -> None:
577
result = _construct_validity_buffer_from_bytemask(
578
bytemask, null_value=0, allow_copy=True
579
)
580
expected = pl.Series([False, True, True, False])
581
assert_series_equal(result, expected)
582
583
584
def test_construct_validity_buffer_from_bytemask_inverted(
585
bytemask: PolarsBuffer,
586
) -> None:
587
result = _construct_validity_buffer_from_bytemask(
588
bytemask, null_value=1, allow_copy=True
589
)
590
expected = pl.Series([True, False, False, True])
591
assert_series_equal(result, expected)
592
593
594
def test_construct_validity_buffer_from_bytemask_zero_copy_fails(
595
bytemask: PolarsBuffer,
596
) -> None:
597
with pytest.raises(CopyNotAllowedError):
598
_construct_validity_buffer_from_bytemask(
599
bytemask, null_value=0, allow_copy=False
600
)
601
602
603
def test_interchange_protocol_fallback(plmonkeypatch: PlMonkeyPatch) -> None:
604
df_pd = pd.DataFrame({"a": [1, 2, 3]})
605
plmonkeypatch.setattr(df_pd, "__arrow_c_stream__", lambda *args, **kwargs: 1 / 0)
606
with pytest.warns(
607
UserWarning, match="Falling back to Dataframe Interchange Protocol"
608
):
609
result = pl.from_dataframe(df_pd)
610
expected = pl.DataFrame({"a": [1, 2, 3]})
611
assert_frame_equal(result, expected)
612
613
614
def test_to_pandas_int8_20316() -> None:
615
df = pl.Series("a", [None], pl.Int8).to_frame()
616
df_pd = df.to_pandas(use_pyarrow_extension_array=True)
617
result = pl.from_dataframe(df_pd)
618
assert_frame_equal(result, df)
619
620