55import pandas as pd
66import pyarrow as pa
77import xarray as xr
8+ from datafusion .context import ArrowStreamExportable
89
9- Block = t .Dict [str , slice ]
10+ Block = t .Dict [t . Hashable , slice ]
1011Chunks = t .Optional [t .Dict [str , int ]]
1112
1213
@@ -54,7 +55,42 @@ def explode(ds: xr.Dataset, chunks: Chunks = None) -> t.Iterator[xr.Dataset]:
5455
5556
5657def _block_len (block : Block ) -> int :
57- return np .prod ([v .stop - v .start for v in block .values ()])
58+ return int (np .prod ([v .stop - v .start for v in block .values ()]))
59+
60+
61+ def from_map_batched (
62+ func : t .Callable [[...], pd .DataFrame ],
63+ * iterables ,
64+ args : t .Optional [t .Tuple ] = None ,
65+ schema : pa .Schema = None ,
66+ ** kwargs ,
67+ ) -> pa .RecordBatchReader :
68+ """Create a PyArrow RecordBatchReader by mapping a function over iterables.
69+
70+ This is equivalent to dask's from_map but returns a PyArrow
71+ RecordBatchReader that can be used with DataFusion. It iterates over
72+ RecordBatches which are created via the `func` one-at-a-time.
73+
74+ Args:
75+ func: Function to apply to each element of the iterables. Currently, the function
76+ must return a Pandas DataFrame.
77+ *iterables: Iterable objects to map the function over.
78+ schema: Optional schema needed for the RecordBatchReader.
79+ args: Additional positional arguments to pass to func.
80+ **kwargs: Additional keyword arguments to pass to func.
81+
82+ Returns:
83+ A PyArrow RecordBatchReader containing the stream of RecordBatches.
84+ """
85+ if args is None :
86+ args = ()
87+
88+ def map_batches ():
89+ for items in zip (* iterables ):
90+ df = func (* items , * args , ** kwargs )
91+ yield pa .RecordBatch .from_pandas (df , schema = schema )
92+
93+ return pa .RecordBatchReader .from_batches (schema , map_batches ())
5894
5995
6096def from_map (
@@ -109,7 +145,12 @@ def from_map(
109145 return pa .concat_tables (results )
110146
111147
112- def read_xarray (ds : xr .Dataset , chunks : Chunks = None ) -> pa .Table :
148+ def pivot (ds : xr .Dataset ) -> pd .DataFrame :
149+ """Converts an xarray Dataset to a pandas DataFrame."""
150+ return ds .to_dataframe ().reset_index ()
151+
152+
153+ def read_xarray (ds : xr .Dataset , chunks : Chunks = None ) -> ArrowStreamExportable :
113154 """Pivots an Xarray Dataset into a PyArrow Table, partitioned by chunks.
114155
115156 Args:
@@ -128,7 +169,11 @@ def read_xarray(ds: xr.Dataset, chunks: Chunks = None) -> pa.Table:
128169
129170 blocks = list (block_slices (ds , chunks ))
130171
131- def pivot (b : Block ) -> pd .DataFrame :
132- return ds .isel (b ).to_dataframe ().reset_index ()
172+ def pivot_block (b : Block ):
173+ return pivot (ds .isel (b ))
174+
175+ schema = pa .Schema .from_pandas (pivot_block (blocks [0 ]))
176+ last_schema = pa .Schema .from_pandas (pivot_block (blocks [- 1 ]))
177+ assert schema == last_schema , "Schemas must be consistent across blocks!"
133178
134- return from_map ( pivot , blocks )
179+ return from_map_batched ( pivot_block , blocks , schema = schema )
0 commit comments