Странное поведение Enumerate при использовании панд данных - PullRequest
0 голосов
/ 12 ноября 2018

У меня есть кадр данных (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)]

Как мне этого добиться?

Ответы [ 3 ]

0 голосов
/ 12 ноября 2018

Это не имеет ничего общего с enumerate, это красная сельдь.Проблема в том, что вы ищете вывод смешанного типа, в то время как Pandas предпочитает хранить однородные данные.

То, что вы ищете, это не , рекомендуемое для 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
0 голосов
/ 12 ноября 2018

Ваш df2 имеет смешанные типы:

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 приведет к повышению до наименьшего общего знаменателя От доц :

dtype будет dtype с общим общим знаменателем (неявный приведение к базовому типу); то есть, если 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)]

Но на самом деле в этом нет необходимости.

0 голосов
/ 12 ноября 2018

Проблема заключается в том, что вызов df2.values приведет к тому, что данные df2 будут возвращены в виде пустого массива с одним типом d, где все целые числа также приводятся к float.

Вы можете предотвратить это принуждение, используя 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)]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...