将数据框转换为rec数组(将对象转换为字符串)
Posted
技术标签:
【中文标题】将数据框转换为rec数组(将对象转换为字符串)【英文标题】:Convert dataframe to a rec array (and objects to strings) 【发布时间】:2019-03-05 21:12:53 【问题描述】:我有一个带有混合数据类型(dtypes)的 pandas 数据框,我希望将其转换为 numpy 结构化数组(或记录数组,在这种情况下基本相同)。对于纯数字数据帧,使用 to_records()
方法很容易做到这一点。我还需要将 pandas 列的 dtypes 转换为 strings 而不是 objects 以便我可以使用 numpy 方法 tofile()
将数字和字符串输出到二进制文件文件,但不会输出对象。
简而言之,我需要将带有 dtype=object
的 pandas 列转换为字符串或 unicode dtype 的 numpy 结构化数组。
这是一个示例,如果所有列都有数字(浮点数或整数)dtype,代码就足够了。
import pandas as pd
df=pd.DataFrame('f_num': [1.,2.,3.], 'i_num':[1,2,3],
'char': ['a','bb','ccc'], 'mixed':['a','bb',1])
struct_arr=df.to_records(index=False)
print('struct_arr',struct_arr.dtype,'\n')
# struct_arr (numpy.record, [('f_num', '<f8'), ('i_num', '<i8'),
# ('char', 'O'), ('mixed', 'O')])
但因为我想以字符串 dtype 结尾,所以我需要添加这个额外且有些涉及的代码:
lst=[]
for col in struct_arr.dtype.names: # this was the only iterator I
# could find for the column labels
dt=struct_arr[col].dtype
if dt == 'O': # this is 'O', meaning 'object'
# it appears an explicit string length is required
# so I calculate with pandas len & max methods
dt = 'U' + str( df[col].astype(str).str.len().max() )
lst.append((col,dt))
struct_arr = struct_arr.astype(lst)
print('struct_arr',struct_arr.dtype)
# struct_arr (numpy.record, [('f_num', '<f8'), ('i_num', '<i8'),
# ('char', '<U3'), ('mixed', '<U2')])
另请参阅:How to change the dtype of certain columns of a numpy recarray?
这似乎有效,因为字符和混合 dtypes 现在是 <U3
和 <U2
而不是 'O' 或 'object'。我只是在检查是否有更简单或更优雅的方法。但是由于 pandas 没有像 numpy 那样的原生字符串类型,也许没有?
【问题讨论】:
dt=df['mixed'].values.astype(str).dtype
为我工作。
我很想蚕食to_records
,并结合您的 dtype 转换。它在列上进行迭代,并使用np.rec.fromarrays
构建数组。
你看过那个函数的代码了吗?
我认为'cannibalize'更常用于机械,例如失事的飞机,而不是编程和功能。
@hpaulj 谢谢,这是一个很好的建议,我在我自己的问题的回答中加入了。 'cannibalize'也是一个很好的用法,我刚开始没有明白这个意思。 ;-)
【参考方案1】:
据我所知,没有本机功能。例如,一个系列中所有值的最大长度不会存储在任何地方。
但是,您可以通过列表推导和 f 字符串更有效地实现您的逻辑:
data_types = [(col, arr[col].dtype if arr[col].dtype != 'O' else \
f'Udf[col].astype(str).str.len().max()') for col in arr.dtype.names]
【讨论】:
谢谢!我讨厌检查我自己的答案,但这最终只是我需要的一半(虽然非常不错;-)【参考方案2】:结合来自@jpp(为了简洁列出comp)和@hpaulj(为了速度而蚕食to_records
)的建议,我想出了以下代码,它是更简洁的代码,也比我的原始代码快约5倍(通过扩展测试上面的示例数据框到 10,000 行):
names = df.columns
arrays = [ df[col].get_values() for col in names ]
formats = [ array.dtype if array.dtype != 'O'
else f'array.astype(str).dtype' for array in arrays ]
rec_array = np.rec.fromarrays( arrays, dtype='names': names, 'formats': formats )
上面将输出 unicode 而不是字符串,这通常可能更好,但在我的情况下,我需要转换为字符串,因为我正在读取 fortran 中的二进制文件,而字符串似乎更容易读入。因此,最好将上面的“格式”行替换为:
formats = [ array.dtype if array.dtype != 'O'
else array.astype(str).dtype.str.replace('<U','S') for array in arrays ]
例如<U4
的 dtype 变为 S4
。
【讨论】:
以上是关于将数据框转换为rec数组(将对象转换为字符串)的主要内容,如果未能解决你的问题,请参考以下文章
将带有 JSON 对象数组的 Spark 数据框列转换为多行
Pandas 数据框无法将列数据类型从对象转换为字符串以进行进一步操作