Могу ли я сохранить файл Parquet со столбцом словаря со смешанными типами значений? - PullRequest
2 голосов
/ 05 августа 2020

Я пытаюсь сохранить Python Pandas DataFrame как файл Parquet, но у меня возникают некоторые проблемы. Один из столбцов моего Pandas DF содержит словари как таковые:

import pandas as pandas

df = pd.DataFrame({
    "ColA": [1, 2, 3],
    "ColB": ["X", "Y", "Z"],
    "ColC": [
        { "Field": "Value" },
        { "Field": "Value2" },
        { "Field": "Value3" }
    ]
})

df.to_parquet("test.parquet")

Теперь это работает отлично, проблема в том, что одно из вложенных значений словаря имеет другой тип, чем остальные . Например:

import pandas as pandas

df = pd.DataFrame({
    "ColA": [1, 2, 3],
    "ColB": ["X", "Y", "Z"],
    "ColC": [
        { "Field": "Value" },
        { "Field": "Value2" },
        { "Field": ["Value3"] }
    ]
})

df.to_parquet("test.parquet")

Это вызывает следующую ошибку:

ArrowInvalid: ('cannot mix list and non-list, non-null values', 'Conversion failed for column ColC with type object')

Обратите внимание, как для последней строки DF свойство Field словарь ColC представляет собой список, а не строку.

Есть ли какой-нибудь обходной путь, чтобы сохранить этот DF как файл Parquet?

1 Ответ

2 голосов
/ 10 августа 2020

ColC - это UDT (определяемый пользователем тип) с одним полем с именем Field типа Union of String, List of String.

Теоретически стрелка поддерживает его, но на практике ему сложно понять, что тип ColC есть. Даже если бы вы явно указали схему своего фрейма данных, это не сработает, потому что этот тип преобразования (преобразование объединений из pandas в стрелку / паркет) еще не поддерживается.

union_type = pa.union(
    [pa.field("0",pa.string()), pa.field("1", pa.list_(pa.string()))],
    'dense'
)
col_c_type = pa.struct(
    [
        pa.field('Field', union_type)
    ]
)

schema=pa.schema(
    [
        pa.field('ColA', pa.int32()),
        pa.field('ColB', pa.string()),
        pa.field('ColC', col_c_type),
    ]
)

df = pd.DataFrame({
    "ColA": [1, 2, 3],
    "ColB": ["X", "Y", "Z"],
    "ColC": [
        { "Field": "Value" },
        { "Field": "Value2" },
        { "Field": ["Value3"] }
    ]
})

pa.Table.from_pandas(df, schema)

Это дает вам следующую ошибку:

('Sequence converter for type union[dense]<0: string=0, 1: list<item: string>=1> not implemented', 'Conversion failed for column ColC with type object'

Даже если вы создадите таблицу со стрелками вручную, она не сможет преобразовать ее в паркет (опять же, объединение не поддерживается).

import io
import pyarrow.parquet as pq

col_a = pa.array([1, 2, 3], pa.int32())
col_b = pa.array(["X", "Y", "Z"], pa.string())

xs = pa.array(["Value", "Value2", None], type=pa.string())
ys = pa.array([None, None, ["value3"]], type=pa.list_(pa.string()))
types = pa.array([0, 0, 1], type=pa.int8())

col_c = pa.UnionArray.from_sparse(types, [xs, ys])

table = pa.Table.from_arrays(
    [col_a, col_b, col_c],
    schema=pa.schema([
        pa.field('ColA', col_a.type),
        pa.field('ColB', col_b.type),
        pa.field('ColC', col_c.type),
    ])
)

with io.BytesIO() as buffer:
    pq.write_table(table, buffer)
Unhandled type for Arrow to Parquet schema conversion: sparse_union<0: string=0, 1: list<item: string>=1>

Я думаю, что ваш единственный вариант на данный момент - это использовать структуру, в которой поля имеют разные имена для строкового значения и списка строковых значений.

df = pd.DataFrame({
    "ColA": [1, 2, 3],
    "ColB": ["X", "Y", "Z"],
    "ColC": [
        { "Field1": "Value" },
        { "Field1": "Value2" },
        { "Field2": ["Value3"] }
    ]
})

df.to_parquet('/tmp/hello')
...