Python 3.x 如何更新pyarrow表中的数据?

Python 3.x 如何更新pyarrow表中的数据?,python-3.x,pyarrow,Python 3.x,Pyarrow,我有一个python脚本,它使用pyarrow读取拼花地板文件。我正试图在表中循环以更新其中的值。如果我尝试这样做: for col_name in table2.column_names: if col_name in my_columns: print('updating values in column ' + col_name) col_data = pa.Table.column(table2, col_name)

我有一个python脚本,它使用pyarrow读取拼花地板文件。我正试图在表中循环以更新其中的值。如果我尝试这样做:

for col_name in table2.column_names:
    if col_name in my_columns:
        print('updating values in column '  + col_name)
        
        col_data = pa.Table.column(table2, col_name)
        
        row_ct = 1
        for i in col_data:
            pa.Table.column(table2, col_name)[row_ct] = change_str(pa.StringScalar.as_py(i))
            row_ct += 1
我得到这个错误:

 TypeError: 'pyarrow.lib.ChunkedArray' object does not support item assignment
如何更新这些值


我尝试使用pandas,但它无法处理原始表中的null值,而且它还错误地转换了原始表中列的数据类型。pyarrow是否有编辑数据的本机方法?

pyarrow中更新数组数据的本机方法是。转换成熊猫,正如你所描述的,也是实现这一点的有效方法,所以你可能想弄清楚这一点。然而,API不会与您的方法相匹配

当前,您可以在Python函数
change\u str
中决定每个项的新值。希望能够将需要执行的操作表示为pyarrow计算函数的组合。这将避免将整个本机数组编组为python对象的(昂贵)成本。如果你能描述一下你在
change\u str
中试图实现的目标(可能是在一个新问题中),我可以帮你弄清楚

如果出于某种原因,您必须在Python中保留
change\u str
,那么您将需要使用不可变的箭头表(和数组)将整个列转换为Python对象(这将有相当大的性能损失)。因此,您将无法就地更新表

实现这一点的方法是在修改数据时创建数据的副本。Arrow支持对的一些基本操作,但它们非常有限

另一个选择是使用熊猫,但正如您所注意到的,从箭头到熊猫再到背面并不是无缝的

让我们举个例子:

>>> table = pa.Table.from_arrays(
    [ 
        pa.array(['abc', 'def'], pa.string()),
        pa.array([1, None], pa.int32()),
    ],
    schema=pa.schema(
    [
        pa.field('str_col', pa.string()), 
        pa.field('int_col', pa.int32()), 
    ]
    )
)
>>> from_pandas = pa.Table.from_pandas(table.to_pandas())
>>> from_pandas.schema
str_col: string
int_col: double
-- schema metadata --
pandas: '{"index_columns": [{"kind": "range", "name": null, "start": 0, "' + 487
您可以看到,转换为pandas和back将int列的类型更改为double。这是因为pandas不太支持null int值,所以它将int列转换为double

为了避免这个问题,我建议在逐列的基础上工作,只将字符串列转换为熊猫:

def my_func(value):
    return 'hello ' + value + '!'


columns = []
my_columns = ['str_col']
for column_name in table.column_names:
    column_data = table[column_name]
    if column_name in my_columns:
        column_data = pa.array(table['str_col'].to_pandas().apply(my_func))
    columns.append(column_data)

updated_table = pa.Table.from_arrays(
    columns, 
    schema=table.schema
)
>>表格['str_col']
[
[
“你好,abc!”,
“你好!”
]
]

我能够使用以下参考资料使其正常工作:

基本上,它在原始表中循环并创建新列(pa.array),其中包含它附加到新表中的调整后的文本。这可能不是最好的方法,但它奏效了。最重要的是,它允许我保留空值并指定每列的数据类型

import sys, getopt
import random
import re
import math

import pyarrow.parquet as pq
import pyarrow.csv as pcsv
import numpy as np
import pandas as pd
import pyarrow as pa
import os.path

<a lot of other code here>

parquet_file = pq.ParquetFile(in_file)
table2 = pq.read_table(in_file)

<a lot of other code here>

changed_ct = 0
all_cols_ct = 0
table3 = pa.Table.from_arrays([pa.array(range(0,table2.num_rows))], names=('0')) # CREATE TEMP COLUMN!!
#print(table3)
#exit()
changed_column_list = []
for col_name in table2.column_names:
    print('processing column: ' + col_name)
    new_list = []
    col_data = pa.Table.column(table2, col_name)
    col_data_type = table2.schema.field(col_name).type
    printed_changed_flag = False
    for i in col_data:
        # GET STRING REPRESENTATION OF THE COLUMN DATA
        if(col_data_type == 'string'):
            col_str = pa.StringScalar.as_py(i)
        elif(col_data_type == 'int32'):
            col_str = pa.Int32Scalar.as_py(i)
        elif(col_data_type == 'int64'):
            col_str = pa.Int64Scalar.as_py(i)
            
            
        if col_name in change_columns:
            if printed_changed_flag == False:
                print('changing values in column '  + col_name)
                changed_column_list.append(col_name)
                changed_ct += 1
                printed_changed_flag = True

            new_list.append(change_str(col_str))
        
        else:
            new_list.append(col_str)
        
    #set data type for the column
    if(col_data_type == 'string'):
        col_data_type = pa.string()
    elif(col_data_type == 'int32'):
        col_data_type = pa.int32()
    elif(col_data_type == 'int64'):
        col_data_type = pa.int64()
        
    arr = pa.array(new_list, type=col_data_type)
        
    new_field = pa.field(col_name, col_data_type)
    
    table3 = pa.Table.append_column(table3, new_field, arr)
        
    all_cols_ct += 1
    
#for i in table3:
#   print(i)

table3 = pa.Table.remove_column(table3, 0) # REMOVE TEMP COLUMN!!
#print(table2)
#print('-------------------')
#print(table3)
#exit()

print('changed ' + str(changed_ct) + ' columns:')
print(*changed_column_list, sep='\n')

# WRITE NEW PARQUET FILE
pa.parquet.write_table(table3, out_file)
导入系统,获取选项 随机输入 进口稀土 输入数学 导入pyarrow.parquet作为pq 将pyarrow.csv导入为pcsv 将numpy作为np导入 作为pd进口熊猫 将pyarrow作为pa导入 导入操作系统路径 拼花文件=pq.ParquetFile(在拼花文件中) 表2=pq.read\u表(在\u文件中) 已更改\u ct=0 所有列数均为0 table3=pa.Table.from_数组([pa.array(range(0,table2.num_行))),name=('0'))35;创建临时列!! #印刷品(表3) #退出() 更改的列列表=[] 对于表2中的列名称。列名称: 打印('处理列:'+列名称) 新列表=[] col_data=pa.Table.column(表2,col_名称) col\u data\u type=table2.schema.field(col\u name).type 已打印\u已更改\u标志=错误 对于i列中的数据: #获取列数据的字符串表示形式 如果(列数据类型=='string'): col_str=pa.StringScalar.as_py(i) elif(列数据类型='int32'): col_str=pa.Int32Scalar.as_py(i) elif(列数据类型='int64'): col_str=pa.Int64Scalar.as_py(i) 如果更改列中的列名称: 如果已打印\u已更改\u标志==False: 打印('更改列'+列名称中的值) 更改了列列表。追加(列名称) 更改的\u ct+=1 已打印\u已更改\u标志=真 新增列表。追加(更改列表(列列表)) 其他: 新列表。追加(col\u str) #设置列的数据类型 如果(列数据类型=='string'): col_data_type=pa.string() elif(列数据类型='int32'): col_data_type=pa.int32() elif(列数据类型='int64'): col_data_type=pa.int64() arr=pa.array(新列表,类型=列数据类型) 新建字段=pa.field(列名称、列数据类型) 表3=pa.Table.append_列(表3,新_字段,arr) 所有列数均大于等于1 #对于表3中的i: #印刷品(一) 表3=pa.Table.删除_列(表3,0)#删除临时列!! #印刷品(表2) #打印('-------------') #印刷品(表3) #退出() 打印('changed'+str(changed_ct)+'列:') 打印(*已更改列列表,sep='\n') #编写新的拼花文件 pa.parquet.写表(表3,外文件)
我最终想出了与您建议的使用pa.array相同的方法,而且效果非常好。我发布了我的答案,但我会将你的答案标记为“官方”答案,因为它非常相似。谢谢
import sys, getopt
import random
import re
import math

import pyarrow.parquet as pq
import pyarrow.csv as pcsv
import numpy as np
import pandas as pd
import pyarrow as pa
import os.path

<a lot of other code here>

parquet_file = pq.ParquetFile(in_file)
table2 = pq.read_table(in_file)

<a lot of other code here>

changed_ct = 0
all_cols_ct = 0
table3 = pa.Table.from_arrays([pa.array(range(0,table2.num_rows))], names=('0')) # CREATE TEMP COLUMN!!
#print(table3)
#exit()
changed_column_list = []
for col_name in table2.column_names:
    print('processing column: ' + col_name)
    new_list = []
    col_data = pa.Table.column(table2, col_name)
    col_data_type = table2.schema.field(col_name).type
    printed_changed_flag = False
    for i in col_data:
        # GET STRING REPRESENTATION OF THE COLUMN DATA
        if(col_data_type == 'string'):
            col_str = pa.StringScalar.as_py(i)
        elif(col_data_type == 'int32'):
            col_str = pa.Int32Scalar.as_py(i)
        elif(col_data_type == 'int64'):
            col_str = pa.Int64Scalar.as_py(i)
            
            
        if col_name in change_columns:
            if printed_changed_flag == False:
                print('changing values in column '  + col_name)
                changed_column_list.append(col_name)
                changed_ct += 1
                printed_changed_flag = True

            new_list.append(change_str(col_str))
        
        else:
            new_list.append(col_str)
        
    #set data type for the column
    if(col_data_type == 'string'):
        col_data_type = pa.string()
    elif(col_data_type == 'int32'):
        col_data_type = pa.int32()
    elif(col_data_type == 'int64'):
        col_data_type = pa.int64()
        
    arr = pa.array(new_list, type=col_data_type)
        
    new_field = pa.field(col_name, col_data_type)
    
    table3 = pa.Table.append_column(table3, new_field, arr)
        
    all_cols_ct += 1
    
#for i in table3:
#   print(i)

table3 = pa.Table.remove_column(table3, 0) # REMOVE TEMP COLUMN!!
#print(table2)
#print('-------------------')
#print(table3)
#exit()

print('changed ' + str(changed_ct) + ' columns:')
print(*changed_column_list, sep='\n')

# WRITE NEW PARQUET FILE
pa.parquet.write_table(table3, out_file)