我有一个数据框(df):
df = pd.DataFrame({'a':[1],'l':[2],'m':[3],'k':[4],'s':[5],'f':[6]},index=[0])
我在行上使用枚举。
res = [tuple(x) for x in enumerate(df.values)]
print(res)
>>> [(1, 1, 6, 4, 2, 3, 5)] ### the elements are int type
现在,当我更改数据框df的一列的数据类型时:
df2 = pd.DataFrame({'a':[1],'l':[2],'m':[3],'k':[4],'s':[5.5],'f':[6]},index=[0])
再次使用枚举,我得到:
res2 = [tuple(x) for x in enumerate(df2.values)]
print(res2)
>>> [(1, 1.0, 6.0, 4.0, 2.0, 3.0, 5.5)] ### the elements data type has changed
我不明白为什么?
我也在寻找一种解决方案,我必须将其转换为自己的数据类型。 例如。
res = [(1, 1, 6, 4, 2, 3, 5.5)]
我该如何实现?
答案 0 :(得分:4)
这与enumerate
无关,那是一条红鲱鱼。问题是您正在寻找混合类型的输出,而Pandas更喜欢存储同类数据。
不推荐与熊猫一起寻找。您的数据类型应为int
或float
,而不是组合。这对性能有影响,因为唯一的直接选择就是使用object
dtype系列,该系列仅允许在Python时间内进行操作。转换为object
dtype效率不高。
这就是您可以做到的:
res2 = df2.astype(object).values.tolist()[0]
print(res2)
[1, 6, 4, 2, 3, 5.5]
一种避免object
转换的方法:
from itertools import chain
from operator import itemgetter, methodcaller
iter_series = map(itemgetter(1), df2.items())
res2 = list(chain.from_iterable(map(methodcaller('tolist'), iter_series)))
[1, 6, 4, 2, 3, 5.5]
性能基准化
如果要输出一个元组列表 ,每行一个元组,则基于序列的解决方案的性能会更好:-
# Python 3.6.0, Pandas 0.19.2
df2 = pd.DataFrame({'a':[1],'l':[2],'m':[3],'k':[4],'s':[5.5],'f':[6]},index=[0])
from itertools import chain
from operator import itemgetter, methodcaller
n = 10**5
df2 = pd.concat([df2]*n)
def jpp_series(df2):
iter_series = map(itemgetter(1), df2.items())
return list(zip(*map(methodcaller('tolist'), iter_series)))
def jpp_object1(df2):
return df2.astype(object).values.tolist()
def jpp_object2(df2):
return list(map(tuple, df2.astype(object).values.tolist()))
assert jpp_series(df2) == jpp_object2(df2)
%timeit jpp_series(df2) # 39.7 ms per loop
%timeit jpp_object1(df2) # 43.7 ms per loop
%timeit jpp_object2(df2) # 68.2 ms per loop
答案 1 :(得分:3)
问题在于,调用df2.values
将导致df2
的数据作为具有单个dtype的numpy数组返回,其中所有整数也被强制浮点数。
您可以通过对object
数组进行操作来防止这种强制。
使用astype(object)
将基础的numpy数组转换为对象并防止强制类型:
>>> [(i, *x) for i, x in df2.astype(object).iterrows()]
[(0, 1, 2, 3, 4, 5.5, 6)]
或者,
>>> [(i, *x) for i, x in enumerate(df2.astype(object).values)]
[(0, 1, 2, 3, 4, 5.5, 6)]
或者,在旧版本中,
>>> [(i,) + tuple(x) for i, x in enumerate(df2.astype(object).values)]
[(0, 1, 2, 3, 4, 5.5, 6)]
答案 2 :(得分:2)
您的df2
具有混合的dtypes:
In [23]: df2 = pd.DataFrame({'a':[1],'l':[2],'m':[3],'k':[4],'s':[5.5],'f':[6]},index=[0])
...:
In [24]: df2.dtypes
Out[24]:
a int64
f int64
k int64
l int64
m int64
s float64
dtype: object
因此,使用.values
将“转换”为最低的公分母。来自the doces:
dtype将是一个较低的公分母dtype(隐式 cast)也就是说,如果dtypes(甚至是数字类型)是 混合,将容纳所有。搭配使用 关心是否不处理这些块。
看起来就像您实际上只想要.itertuples
:
In [25]: list(df2.itertuples())
Out[25]: [Pandas(Index=0, a=1, f=6, k=4, l=2, m=3, s=5.5)]
请注意,此方便地返回一个namedtuple
对象的列表,如果您真的只想要普通元组,请将tuple
映射到它:
In [26]: list(map(tuple, df2.itertuples()))
Out[26]: [(0, 1, 6, 4, 2, 3, 5.5)]
但是真的没有必要。