Панды конвертируют dataframe в массив кортежей - PullRequest
93 голосов
/ 18 марта 2012

Я манипулировал некоторыми данными с помощью панд, и теперь я хочу выполнить пакетное сохранение обратно в базу данных. Это требует, чтобы я преобразовал фрейм данных в массив кортежей, каждый из которых соответствует «строке» фрейма данных.

Мой DataFrame выглядит примерно так:

In [182]: data_set
Out[182]: 
  index data_date   data_1  data_2
0  14303 2012-02-17  24.75   25.03 
1  12009 2012-02-16  25.00   25.07 
2  11830 2012-02-15  24.99   25.15 
3  6274  2012-02-14  24.68   25.05 
4  2302  2012-02-13  24.62   24.77 
5  14085 2012-02-10  24.38   24.61 

Я хочу преобразовать его в массив кортежей вроде:

[(datetime.date(2012,2,17),24.75,25.03),
(datetime.date(2012,2,16),25.00,25.07),
...etc. ]

Любое предложение о том, как я могу эффективно сделать это?

Ответы [ 9 ]

155 голосов
/ 19 марта 2012

Как насчет:

subset = data_set[['data_date', 'data_1', 'data_2']]
tuples = [tuple(x) for x in subset.values]
91 голосов
/ 01 января 2016
list(data_set.itertuples(index=False))

Начиная с 17.1, приведенное выше вернет список именованных кортежей .

Если вы хотите получить список обычных кортежей, передайте name=None в качестве аргумента:

list(data_set.itertuples(index=False, name=None))
42 голосов
/ 05 декабря 2012

Общий способ:

[tuple(x) for x in data_set.to_records(index=False)]
15 голосов
/ 04 июня 2017

Мотивация
Многие наборы данных достаточно велики, поэтому нам нужно заботиться о скорости / эффективности.Поэтому я предлагаю это решение в этом духе.Это также кратко.

Для сравнения давайте отбросим столбец index

df = data_set.drop('index', 1)

Решение
Я предложуиспользование zip и понимание

list(zip(*[df[c].values.tolist() for c in df]))

[('2012-02-17', 24.75, 25.03),
 ('2012-02-16', 25.0, 25.07),
 ('2012-02-15', 24.99, 25.15),
 ('2012-02-14', 24.68, 25.05),
 ('2012-02-13', 24.62, 24.77),
 ('2012-02-10', 24.38, 24.61)]

Это также может быть гибким, если мы хотим иметь дело с определенным подмножеством столбцов.Мы предполагаем, что столбцы, которые мы уже отобразили, являются подмножеством, которое мы хотим.

list(zip(*[df[c].values.tolist() for c in ['data_date', 'data_1', 'data_2']))

[('2012-02-17', 24.75, 25.03),
 ('2012-02-16', 25.0, 25.07),
 ('2012-02-15', 24.99, 25.15),
 ('2012-02-14', 24.68, 25.05),
 ('2012-02-13', 24.62, 24.77),
 ('2012-02-10', 24.38, 24.61)]

Все последующие дают одинаковые результаты

  • [tuple(x) for x in df.values]
  • df.to_records(index=False).tolist()
  • list(map(tuple,df.values))
  • list(map(tuple, df.itertuples(index=False)))

Что быстрее?
zip и понимание быстрее с большим отрывом

%timeit [tuple(x) for x in df.values]
%timeit list(map(tuple, df.itertuples(index=False)))
%timeit df.to_records(index=False).tolist()
%timeit list(map(tuple,df.values))
%timeit list(zip(*[df[c].values.tolist() for c in df]))

небольшие данные

10000 loops, best of 3: 55.7 µs per loop
1000 loops, best of 3: 596 µs per loop
10000 loops, best of 3: 38.2 µs per loop
10000 loops, best of 3: 54.3 µs per loop
100000 loops, best of 3: 12.9 µs per loop

большие данные

10 loops, best of 3: 58.8 ms per loop
10 loops, best of 3: 43.9 ms per loop
10 loops, best of 3: 29.3 ms per loop
10 loops, best of 3: 53.7 ms per loop
100 loops, best of 3: 6.09 ms per loop
9 голосов
/ 20 декабря 2016

Вот векторизованный подход (при условии, что вместо данных data_set будет определен кадр данных, *1001*), который возвращает list из tuples, как показано:

>>> df.set_index(['data_date'])[['data_1', 'data_2']].to_records().tolist()

производит:

[(datetime.datetime(2012, 2, 17, 0, 0), 24.75, 25.03),
 (datetime.datetime(2012, 2, 16, 0, 0), 25.0, 25.07),
 (datetime.datetime(2012, 2, 15, 0, 0), 24.99, 25.15),
 (datetime.datetime(2012, 2, 14, 0, 0), 24.68, 25.05),
 (datetime.datetime(2012, 2, 13, 0, 0), 24.62, 24.77),
 (datetime.datetime(2012, 2, 10, 0, 0), 24.38, 24.61)]

Идея установки столбца datetime в качестве оси индекса состоит в том, чтобы помочь преобразовать значение Timestamp в его соответствующий эквивалент формата datetime.datetime, используя аргумент convert_datetime64 в DF.to_records, что делает это для DateTimeIndex кадра данных.

Возвращает recarray, который затем можно сделать, чтобы вернуть list, используя .tolist


Более обобщенное решение в зависимости от варианта использования будет:

df.to_records().tolist()                              # Supply index=False to exclude index
6 голосов
/ 01 марта 2019

Этот ответ не добавляет ответы, которые еще не обсуждались, но вот некоторые результаты скорости.Я думаю, что это должно решить вопросы, которые возникли в комментариях.Все они выглядят так, как будто они O (n) , основываясь на этих трех значениях.

TL; DR : tuples = list(df.itertuples(index=False, name=None)) и tuples = list(zip(*[df[c].values.tolist() for c in df])) связаны длясамый быстрый.

Я сделал быстрый тест скорости на результаты для трех предложений здесь:

  1. Почтовый ответ @pirsquared: tuples = list(zip(*[df[c].values.tolist() for c in df]))
  2. Принятый ответот @ wes-mckinney: tuples = [tuple(x) for x in df.values]
  3. Ответ itertuples от @ksindi с предложением name=None от @Axel: tuples = list(df.itertuples(index=False, name=None))
from numpy import random
import pandas as pd


def create_random_df(n):
    return pd.DataFrame({"A": random.randint(n, size=n), "B": random.randint(n, size=n)})

Маленький размер:

df = create_random_df(10000)
%timeit tuples = list(zip(*[df[c].values.tolist() for c in df]))
%timeit tuples = [tuple(x) for x in df.values]
%timeit tuples = list(df.itertuples(index=False, name=None))

Дает:

1.66 ms ± 200 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
15.5 ms ± 1.52 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
1.74 ms ± 75.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Больше:

df = create_random_df(1000000)
%timeit tuples = list(zip(*[df[c].values.tolist() for c in df]))
%timeit tuples = [tuple(x) for x in df.values]
%timeit tuples = list(df.itertuples(index=False, name=None))

Дает:

202 ms ± 5.91 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
1.52 s ± 98.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
209 ms ± 11.8 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

Столько терпения, сколько у меня есть:

df = create_random_df(10000000)
%timeit tuples = list(zip(*[df[c].values.tolist() for c in df]))
%timeit tuples = [tuple(x) for x in df.values]
%timeit tuples = list(df.itertuples(index=False, name=None))

Дает:

1.78 s ± 118 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
15.4 s ± 222 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
1.68 s ± 96.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Версия zip и версия itertuples находятся в пределах доверительных интервалов друг для друга.Я подозреваю, что они делают то же самое под капотом.

Эти тесты скорости, вероятно, не имеют значения.Расширение памяти моего компьютера не займет много времени, и вы действительно не должны делать это на большом наборе данных.Работа с этими кортежами после этого может оказаться действительно неэффективной.Вряд ли это станет основным узким местом в вашем коде, поэтому просто придерживайтесь версии, которую вы считаете наиболее читаемой.

3 голосов
/ 12 апреля 2019

Самый эффективный и простой способ:

list(data_set.to_records())

Вы можете отфильтровать нужные столбцы перед этим вызовом.

2 голосов
/ 09 октября 2017
#try this one:

tuples = list(zip(data_set["data_date"], data_set["data_1"],data_set["data_2"]))
print (tuples)
2 голосов
/ 17 апреля 2017

Более питонический путь:

df = data_set[['data_date', 'data_1', 'data_2']]
map(tuple,df.values)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...