Skip to content
Closed
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
cd3d51e
createDataFrame working but with fixed schema in python
BryanCutler Sep 29, 2017
c73c7c6
added schema conversion
BryanCutler Oct 4, 2017
e9c6de7
add from_arrow_schema, test and cleanup
BryanCutler Oct 9, 2017
06b033f
fix style
BryanCutler Oct 9, 2017
9d667c6
fixed xrange for Python 3
BryanCutler Oct 10, 2017
31851f8
Merge remote-tracking branch 'upstream/master' into arrow-createDataF…
BryanCutler Oct 10, 2017
ca474db
moved python jvm call to PythonSQLUtils, added tearDownClass to tests
BryanCutler Oct 10, 2017
c7ddee6
forgot to rename conf
BryanCutler Oct 10, 2017
b00a924
fixed typo
BryanCutler Oct 13, 2017
e36a176
using schema if passed in to createDataFrame, added unit test to veri…
BryanCutler Oct 14, 2017
fc3a554
Merge remote-tracking branch 'upstream/master' into arrow-createDataF…
BryanCutler Oct 14, 2017
f42e351
updated function name to_arrow_type
BryanCutler Oct 14, 2017
76e87dc
revert DataFrame schema arg, added test for wrong schema, fixed typos
BryanCutler Oct 16, 2017
81ddfa9
moved common code between parallelize to _serialize_to_jvm
BryanCutler Oct 18, 2017
5e8e11f
when schema provided, attempt to cast series and fallback if not matc…
BryanCutler Oct 18, 2017
3052f30
added support for schema as list of names
BryanCutler Oct 18, 2017
9f7b1c0
Simplify `_createFromPandasWithArrow()`.
ueshin Oct 19, 2017
dc03657
changed to use izip
BryanCutler Oct 24, 2017
f421e2d
added check for case of specifying schema with like 'int'
BryanCutler Oct 24, 2017
0de3126
changed single type to fallback and error
BryanCutler Oct 24, 2017
c41cf33
Merge remote-tracking branch 'upstream/master' into arrow-createDataF…
BryanCutler Oct 27, 2017
b6df7bf
add support for date and timestamp for from_arrow_type
BryanCutler Oct 27, 2017
cfb1c3d
using _create_batch to make arrow batches also without explicit schem…
BryanCutler Oct 30, 2017
b362b9a
Merge remote-tracking branch 'upstream/master' into arrow-createDataF…
BryanCutler Nov 7, 2017
1c244d1
some minor cleanup of _convert_from_pandas
BryanCutler Nov 7, 2017
99ce1e4
minor cleanup of _create_from_pandas_with_arrow
BryanCutler Nov 7, 2017
7d9cc3e
avoid double copies of series with nulls
BryanCutler Nov 9, 2017
126f2e7
added test to make sure input is unchanged
BryanCutler Nov 9, 2017
421d0be
removed copy=True option, did not improve anything
BryanCutler Nov 9, 2017
0ad736b
fix pydoc
BryanCutler Nov 9, 2017
6c72e37
added schema tuple support, refactored creating schema
BryanCutler Nov 10, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions python/pyspark/java_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ def killChild():
java_import(gateway.jvm, "org.apache.spark.mllib.api.python.*")
# TODO(davies): move into sql
java_import(gateway.jvm, "org.apache.spark.sql.*")
java_import(gateway.jvm, "org.apache.spark.sql.api.python.*")
java_import(gateway.jvm, "org.apache.spark.sql.hive.*")
java_import(gateway.jvm, "scala.Tuple2")

Expand Down
4 changes: 2 additions & 2 deletions python/pyspark/sql/dataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,12 @@ class DataFrame(object):
.. versionadded:: 1.3
"""

def __init__(self, jdf, sql_ctx):
def __init__(self, jdf, sql_ctx, schema=None):
self._jdf = jdf
self.sql_ctx = sql_ctx
self._sc = sql_ctx and sql_ctx._sc
self.is_cached = False
self._schema = None # initialized lazily
self._schema = schema # initialized lazily if None

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BryanCutler, what do you think about taking this out back? Maybe, I am too much worried but I think we maybe should avoid it to be assigned actually except for few special cases ...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I can undo it. I don't really like manually assigning the schema after the constructor but it's just done in these 2 special cases..

self._lazy_rdd = None

@property
Expand Down
52 changes: 46 additions & 6 deletions python/pyspark/sql/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

if sys.version >= '3':
basestring = unicode = str
xrange = range
else:
from itertools import imap as map

Expand Down Expand Up @@ -414,6 +415,43 @@ def _createFromLocal(self, data, schema):
data = [schema.toInternal(row) for row in data]
return self._sc.parallelize(data), schema

def _createFromPandasWithArrow(self, df, schema):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: df -> pdf.

"""
Create a DataFrame from a given pandas.DataFrame by slicing the into partitions, converting

@viirya viirya Oct 15, 2017

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: slicing the ...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! fixed

to Arrow data, then reading into the JVM to parallelsize. If a schema is passed in, the

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: parallelsize -> parallelize?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, fixed!

data types will be used to coerce the data in Pandas to Arrow conversion.
"""
import os
from tempfile import NamedTemporaryFile
from pyspark.serializers import ArrowSerializer
from pyspark.sql.types import from_arrow_schema, to_arrow_schema
import pyarrow as pa

# Slice the DataFrame into batches
step = -(-len(df) // self.sparkContext.defaultParallelism) # round int up
df_slices = (df[start:start + step] for start in xrange(0, len(df), step))
arrow_schema = to_arrow_schema(schema) if schema is not None else None
batches = [pa.RecordBatch.from_pandas(df_slice, schema=arrow_schema, preserve_index=False)
for df_slice in df_slices]

# write batches to temp file, read by JVM (borrowed from context.parallelize)
tempFile = NamedTemporaryFile(delete=False, dir=self._sc._temp_dir)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks kind of duplicate with the main logic of context.parallelize. Maybe we can extract a common function from it.

@BryanCutler BryanCutler Oct 16, 2017

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it is - I didn't want to mess around with the parallelize() logic so I left it alone. If we were to make a common function it would look like this

def _dump_to_tempfile(data, serializer, parallelism):
    tempFile = NamedTemporaryFile(delete=False, dir=self._temp_dir)
    try:
        serializer.dump_stream(c, tempFile)
        tempFile.close()
        readRDDFromFile = self._jvm.PythonRDD.readRDDFromFile
        return readRDDFromFile(self._jsc, tempFile.name, parallelism)
    finally:
        # readRDDFromFile eagerily reads the file so we can delete right after.
        os.unlink(tempFile.name)

and some changes to parallelize to call it

# Make sure we distribute data evenly if it's smaller than self.batchSize
if "__len__" not in dir(c):
    c = list(c)    # Make it a list so we can compute its length
batchSize = max(1, min(len(c) // numSlices, self._batchSize or 1024))
serializer = BatchedSerializer(self._unbatched_serializer, batchSize)
jrdd = _dump_to_tempfile(c, serializer, numSlices)

Let me know if you all think we should change this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer less duplicate. Let's see if others support it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good to me.

try:
serializer = ArrowSerializer()
serializer.dump_stream(batches, tempFile)
tempFile.close()
readRDDFromFile = self._jvm.PythonRDD.readRDDFromFile
jrdd = readRDDFromFile(self._jsc, tempFile.name, len(batches))
finally:
# readRDDFromFile eagerily reads the file so we can delete right after.
os.unlink(tempFile.name)

# Create the Spark DataFrame, there will be at least 1 batch
schema = from_arrow_schema(batches[0].schema)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

schema = from_arrow_schema(batches[0].schema) if schema is None else schema?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This brings up a good point, the schema should usually be the same, but what should the behavior be if the user specifies the wrong schema?

  1. If use the passed in schema and it is wrong (the line in the comment above) then there might be an error later when an operation is performed. For example, if a string column was specified as DoubleType that would produce:
    "Caused by: java.lang.UnsupportedOperationException at org.apache.spark.sql.execution.vectorized.ArrowColumnVector$ArrowVectorAccessor.getDouble(ArrowColumnVector.java:395)"

  2. We could check the user supplied schema matches the ArrowRecordBatch schema and fail immediately if not equal. I'm not sure if there might be some cases where you wouldn't want it to fail - like with different integer types..

  3. Always use the schema from the ArrowRecordBatch (how it is currently in this PR)

I'm thinking that (2) is the best since it's the safest and would produce a clear error message.

@HyukjinKwon HyukjinKwon Oct 17, 2017

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we check ahead and fall back to createDataFrame without Arrow? I think I am seeing some differences in some cases, for example:

import pandas as pd
import numpy as np

spark.conf.set("spark.sql.execution.arrow.enabled", "false")
spark.createDataFrame(pd.DataFrame(data={"dcol": [0.8]}), schema="dcol STRING").show()
spark.conf.set("spark.sql.execution.arrow.enabled", "true")
spark.createDataFrame(pd.DataFrame(data={"dcol": [0.8]}), schema="dcol STRING").show()

WDYT @BryanCutler, @ueshin and @viirya ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There might be some differences because without Arrow, Spark just gets the column names from Pandas and infers the data type. This is by design, but I've seen a lot of users get tripped up by it and create JIRAs. With Arrow, it makes it easy to use the schema from Pandas so I would consider this an improvement.

@HyukjinKwon HyukjinKwon Oct 17, 2017

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I thought we should reduce the diff between createDataFrame with Arrow and createDataFrame without Arrow, and match the behaviour first though. To be clear, my suggestion is:

  1. We could check the user supplied schema matches the ArrowRecordBatch schema and fail immediately if not equal. I'm not sure if there might be some cases where you wouldn't want it to fail - like with different integer types..

but if they are different, fall back to createDataFrame without Arrow to reduce the differences between them. I carefully guess createDataFrame with Arrow is stricter?

@ueshin ueshin Oct 17, 2017

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, do we also need to support schema like ['name', 'age'], "int"(not StructType), etc. from doctest?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @HyukjinKwon , I misread your example. Yes, we can fall back to not use arrow if the user supplied schema doesn't match from arrow - I actually get equal results from your example tho

can we adjust data types if schema is not None

@ueshin we could do this too, I'm not sure what would be better in this case.. I'll think about it for a bit

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm.. FWIW, I get something like:

  File "pyarrow/table.pxi", line 517, in pyarrow.lib.RecordBatch.from_pandas (/.../build/xhochy/pyarrow-macos-wheels/arrow/python/build/temp.macosx-10.6-intel-2.7/lib.cxx:28616)
  File "pyarrow/table.pxi", line 336, in pyarrow.lib._dataframe_to_arrays (/.../build/xhochy/pyarrow-macos-wheels/arrow/python/build/temp.macosx-10.6-intel-2.7/lib.cxx:26836)
  File "pyarrow/array.pxi", line 1157, in pyarrow.lib.Array.from_pandas (/.../build/xhochy/pyarrow-macos-wheels/arrow/python/build/temp.macosx-10.6-intel-2.7/lib.cxx:19825)
  File "pyarrow/error.pxi", line 66, in pyarrow.lib.check_status (/.../build/xhochy/pyarrow-macos-wheels/arrow/python/build/temp.macosx-10.6-intel-2.7/lib.cxx:7157)
pyarrow.lib.ArrowNotImplementedError: NotImplemented: string
>>> import pyarrow
>>> pyarrow.__version__
'0.4.0'
>>> import pandas
>>> pandas.__version__
u'0.20.2'

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh pyarrow 0.4.1 is what is installed on Jenkins, so that is what I've been testing against. Maybe try that version?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmm.. still get the same exception with pyarrow 0.4.1 ..

jdf = self._jvm.PythonSQLUtils.arrowPayloadToDataFrame(
jrdd, schema.json(), self._wrapped._jsqlContext)
return DataFrame(jdf, self._wrapped, schema)

@since(2.0)
@ignore_unicode_prefix
def createDataFrame(self, data, schema=None, samplingRatio=None, verifySchema=True):
Expand Down Expand Up @@ -510,9 +548,13 @@ def createDataFrame(self, data, schema=None, samplingRatio=None, verifySchema=Tr
except Exception:
has_pandas = False
if has_pandas and isinstance(data, pandas.DataFrame):
if schema is None:
schema = [str(x) for x in data.columns]
data = [r.tolist() for r in data.to_records(index=False)]
if self.conf.get("spark.sql.execution.arrow.enabled", "false").lower() == "true" \
and len(data) > 0:
return self._createFromPandasWithArrow(data, schema)
else:
if schema is None:
schema = [str(x) for x in data.columns]
data = [r.tolist() for r in data.to_records(index=False)]

if isinstance(schema, StructType):
verify_func = _make_type_verifier(schema) if verifySchema else lambda _: True
Expand Down Expand Up @@ -541,9 +583,7 @@ def prepare(obj):
rdd, schema = self._createFromLocal(map(prepare, data), schema)
jrdd = self._jvm.SerDeUtil.toJavaArray(rdd._to_java_object_rdd())
jdf = self._jsparkSession.applySchemaToPythonRDD(jrdd.rdd(), schema.json())
df = DataFrame(jdf, self._wrapped)
df._schema = schema
return df
return DataFrame(jdf, self._wrapped, schema)

@ignore_unicode_prefix
@since(2.0)
Expand Down
61 changes: 47 additions & 14 deletions python/pyspark/sql/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3095,16 +3095,32 @@ def setUpClass(cls):
StructField("3_long_t", LongType(), True),
StructField("4_float_t", FloatType(), True),
StructField("5_double_t", DoubleType(), True)])
cls.data = [("a", 1, 10, 0.2, 2.0),
("b", 2, 20, 0.4, 4.0),
("c", 3, 30, 0.8, 6.0)]
cls.data = [(u"a", 1, 10, 0.2, 2.0),
(u"b", 2, 20, 0.4, 4.0),
(u"c", 3, 30, 0.8, 6.0)]

@classmethod
def tearDownClass(cls):
ReusedPySparkTestCase.tearDownClass()
cls.spark.stop()

def assertFramesEqual(self, df_with_arrow, df_without):
msg = ("DataFrame from Arrow is not equal" +
("\n\nWith Arrow:\n%s\n%s" % (df_with_arrow, df_with_arrow.dtypes)) +
("\n\nWithout:\n%s\n%s" % (df_without, df_without.dtypes)))
self.assertTrue(df_without.equals(df_with_arrow), msg=msg)

def createPandasDataFrameFromData(self):
import pandas as pd
import numpy as np
data_dict = {}
for j, name in enumerate(self.schema.names):
data_dict[name] = [self.data[i][j] for i in range(len(self.data))]
# need to convert these to numpy types first
data_dict["2_int_t"] = np.int32(data_dict["2_int_t"])
data_dict["4_float_t"] = np.float32(data_dict["4_float_t"])
return pd.DataFrame(data=data_dict)

def test_unsupported_datatype(self):
schema = StructType([StructField("dt", DateType(), True)])
df = self.spark.createDataFrame([(datetime.date(1970, 1, 1),)], schema=schema)
Expand All @@ -3121,21 +3137,15 @@ def test_null_conversion(self):
def test_toPandas_arrow_toggle(self):
df = self.spark.createDataFrame(self.data, schema=self.schema)
self.spark.conf.set("spark.sql.execution.arrow.enabled", "false")
pdf = df.toPandas()
self.spark.conf.set("spark.sql.execution.arrow.enabled", "true")
try:
pdf = df.toPandas()
finally:
self.spark.conf.set("spark.sql.execution.arrow.enabled", "true")
pdf_arrow = df.toPandas()
self.assertFramesEqual(pdf_arrow, pdf)

def test_pandas_round_trip(self):
import pandas as pd
import numpy as np
data_dict = {}
for j, name in enumerate(self.schema.names):
data_dict[name] = [self.data[i][j] for i in range(len(self.data))]
# need to convert these to numpy types first
data_dict["2_int_t"] = np.int32(data_dict["2_int_t"])
data_dict["4_float_t"] = np.float32(data_dict["4_float_t"])
pdf = pd.DataFrame(data=data_dict)
pdf = self.createPandasDataFrameFromData()
df = self.spark.createDataFrame(self.data, schema=self.schema)
pdf_arrow = df.toPandas()
self.assertFramesEqual(pdf_arrow, pdf)
Expand All @@ -3147,6 +3157,29 @@ def test_filtered_frame(self):
self.assertEqual(pdf.columns[0], "i")
self.assertTrue(pdf.empty)

def test_createDataFrame_toggle(self):
pdf = self.createPandasDataFrameFromData()
self.spark.conf.set("spark.sql.execution.arrow.enabled", "false")
try:
df_no_arrow = self.spark.createDataFrame(pdf)
finally:
self.spark.conf.set("spark.sql.execution.arrow.enabled", "true")
df_arrow = self.spark.createDataFrame(pdf)
self.assertEquals(df_no_arrow.collect(), df_arrow.collect())

def test_createDataFrame_with_schema(self):
pdf = self.createPandasDataFrameFromData()
df = self.spark.createDataFrame(pdf, schema=self.schema)
self.assertEquals(self.schema, df.schema)
pdf_arrow = df.toPandas()
self.assertFramesEqual(pdf_arrow, pdf)

def test_schema_conversion_roundtrip(self):
from pyspark.sql.types import from_arrow_schema, to_arrow_schema
arrow_schema = to_arrow_schema(self.schema)
schema_rt = from_arrow_schema(arrow_schema)
self.assertEquals(self.schema, schema_rt)


@unittest.skipIf(not _have_pandas or not _have_arrow, "Pandas or Arrow not installed")
class VectorizedUDFTests(ReusedPySparkTestCase):
Expand Down
44 changes: 44 additions & 0 deletions python/pyspark/sql/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -1624,6 +1624,50 @@ def to_arrow_type(dt):
return arrow_type


def to_arrow_schema(schema):
""" Convert a schema from Spark to Arrow
"""
import pyarrow as pa
fields = [pa.field(field.name, to_arrow_type(field.dataType)) for field in schema]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add nullable=field.nullable just in case?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, good idea

return pa.schema(fields)


def from_arrow_type(at):
""" Convert pyarrow type to Spark data type.
"""
# TODO: newer pyarrow has is_boolean(at) functions that would be better to check type
import pyarrow as pa
if at == pa.bool_():
spark_type = BooleanType()
elif at == pa.int8():
spark_type = ByteType()
elif at == pa.int16():
spark_type = ShortType()
elif at == pa.int32():
spark_type = IntegerType()
elif at == pa.int64():
spark_type = LongType()
elif at == pa.float32():
spark_type = FloatType()
elif at == pa.float64():
spark_type = DoubleType()
elif type(at) == pa.DecimalType:
spark_type = DecimalType(precision=at.precision, scale=at.scale)
elif at == pa.string():
spark_type = StringType()
else:
raise TypeError("Unsupported type in conversion from Arrow: " + str(at))
return spark_type


def from_arrow_schema(arrow_schema):
""" Convert schema from Arrow to Spark.
"""
return StructType(
[StructField(field.name, from_arrow_type(field.type), nullable=field.nullable)
for field in arrow_schema])


def _test():
import doctest
from pyspark.context import SparkContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@

package org.apache.spark.sql.api.python

import org.apache.spark.api.java.JavaRDD
import org.apache.spark.sql.{DataFrame, SQLContext}
import org.apache.spark.sql.catalyst.analysis.FunctionRegistry
import org.apache.spark.sql.catalyst.expressions.ExpressionInfo
import org.apache.spark.sql.catalyst.parser.CatalystSqlParser
import org.apache.spark.sql.execution.arrow.ArrowConverters
import org.apache.spark.sql.types.DataType

private[sql] object PythonSQLUtils {
Expand All @@ -29,4 +32,19 @@ private[sql] object PythonSQLUtils {
def listBuiltinFunctionInfos(): Array[ExpressionInfo] = {
FunctionRegistry.functionSet.flatMap(f => FunctionRegistry.builtin.lookupFunction(f)).toArray
}

/**
* Python Callable function to convert ArrowPayloads into a [[DataFrame]].
*
* @param payloadRDD A JavaRDD of ArrowPayloads.
* @param schemaString JSON Formatted Schema for ArrowPayloads.
* @param sqlContext The active [[SQLContext]].
* @return The converted [[DataFrame]].
*/
def arrowPayloadToDataFrame(
payloadRDD: JavaRDD[Array[Byte]],
schemaString: String,
sqlContext: SQLContext): DataFrame = {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't believe I found this looks 5 spaces instead of 4.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh man, good catch! I don't know how that happened :\

ArrowConverters.toDataFrame(payloadRDD, schemaString, sqlContext)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import org.apache.arrow.vector.schema.ArrowRecordBatch
import org.apache.arrow.vector.util.ByteArrayReadableSeekableByteChannel

import org.apache.spark.TaskContext
import org.apache.spark.api.java.JavaRDD
import org.apache.spark.sql.{DataFrame, SQLContext}
import org.apache.spark.sql.catalyst.InternalRow
import org.apache.spark.sql.execution.vectorized.{ArrowColumnVector, ColumnarBatch, ColumnVector}
import org.apache.spark.sql.types._
Expand Down Expand Up @@ -203,4 +205,16 @@ private[sql] object ArrowConverters {
reader.close()
}
}

private[sql] def toDataFrame(
payloadRDD: JavaRDD[Array[Byte]],
schemaString: String,
sqlContext: SQLContext): DataFrame = {
val rdd = payloadRDD.rdd.mapPartitions { iter =>
val context = TaskContext.get()
ArrowConverters.fromPayloadIterator(iter.map(new ArrowPayload(_)), context)
}
val schema = DataType.fromJson(schemaString).asInstanceOf[StructType]
sqlContext.internalCreateDataFrame(rdd, schema)
}
}