Gấu trúc chuyển đổi khung dữ liệu thành mảng bộ dữ liệu


131

Tôi đã thao tác một số dữ liệu bằng cách sử dụng gấu trúc và bây giờ tôi muốn thực hiện một đợt lưu lại vào cơ sở dữ liệu. Điều này đòi hỏi tôi phải chuyển đổi khung dữ liệu thành một mảng các bộ dữ liệu, với mỗi bộ dữ liệu tương ứng với một "hàng" của khung dữ liệu.

DataFrame của tôi trông giống như:

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 

Tôi muốn chuyển đổi nó thành một loạt các bộ dữ liệu như:

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

Bất kỳ đề nghị về làm thế nào tôi có thể làm điều này một cách hiệu quả?


21
Đối với những người đến với câu trả lời này trong năm 2017+, có một giải pháp thành ngữ mới dưới đây . Bạn chỉ có thể sử dụnglist(df.itertuples(index=False, name=None))
Ted Petrou

3
Hai điều tôi đang tìm kiếm khi đến với câu hỏi này: Một danh sách các bộ dữ liệu - df.to_records(index=False)và một danh sách các bài viết:df.to_dict('records')
Martin Thoma

@MartinThoma cả to_records và to_dict ('records') vít các kiểu dữ liệu của tôi. Đã biết lỗi nhưng làm cho giải pháp này trở nên vô giá trị ...
Jochen

Câu trả lời:


206

Làm thế nào về:

subset = data_set[['data_date', 'data_1', 'data_2']]
tuples = [tuple(x) for x in subset.to_numpy()]

cho gấu trúc <0,24 sử dụng

tuples = [tuple(x) for x in subset.values]

2
Vui lòng xem câu trả lời của @ ksindi dưới đây để sử dụng .itertuples, sẽ hiệu quả hơn so với việc lấy các giá trị dưới dạng một mảng và thúc đẩy chúng thành một tuple.
vy32

1
sạch hơn một chút là: tuples = map (tuple, subset.values)
RufusVS

Điều này có thể truyền giá trị sang một loại khác, phải không?
AMC

160
list(data_set.itertuples(index=False))

Kể từ ngày 17.1, phần trên sẽ trả về một danh sách các tên được đặt tên .

Nếu bạn muốn một danh sách các bộ dữ liệu thông thường, hãy chuyển qua name=Nonelàm đối số:

list(data_set.itertuples(index=False, name=None))

39
Đây phải là câu trả lời được chấp nhận IMHO (bây giờ đã có một tính năng chuyên dụng). BTW, nếu bạn muốn tuples bình thường trong zipiterator (thay vì namedtuples), hãy gọi:data_set.itertuples(index=False, name=None)
Axel


3
@coldspeed Bài học tôi nhận được từ câu hỏi được liên kết là itertuples chậm vì chuyển đổi sang bộ dữ liệu thường chậm hơn so với các hoạt động véc tơ / cython. Cho rằng câu hỏi đang yêu cầu chuyển đổi sang bộ dữ liệu, có lý do nào khiến chúng tôi nghĩ rằng câu trả lời được chấp nhận là nhanh hơn không? Thử nghiệm nhanh tôi đã làm chỉ ra rằng phiên bản itertuples nhanh hơn.
TC Proctor

2
Tôi đã đăng kết quả kiểm tra tốc độ của mình trong câu trả lời này
TC Proctor

1
@johnDanger nó tương tự như khái niệm eval () và globalals () trong python. Mọi người đều biết chúng tồn tại. Mọi người cũng biết bạn thường không nên sử dụng các chức năng này vì nó được coi là hình thức xấu. Nguyên tắc ở đây là tương tự, có rất ít trường hợp sử dụng họ iter * trong gấu trúc, đây được cho là một trong số đó. Tôi vẫn sẽ sử dụng một phương pháp khác (như danh sách comp hoặc bản đồ) nhưng đó là tôi.
cs95


30

Động lực
Nhiều bộ dữ liệu đủ lớn để chúng ta cần quan tâm đến tốc độ / hiệu quả. Vì vậy, tôi cung cấp giải pháp này trong tinh thần đó. Nó cũng xảy ra ngắn gọn.

Để so sánh, hãy bỏ indexcột

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

Giải pháp
tôi sẽ đề xuất việc sử dụng zipmap

list(zip(*map(df.get, 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)]

Nó cũng xảy ra linh hoạt nếu chúng ta muốn xử lý một tập hợp con cụ thể của các cột. Chúng tôi sẽ giả sử các cột chúng tôi đã hiển thị là tập hợp con chúng tôi muốn.

list(zip(*map(df.get, ['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)]

Nhanh hơn là gì?

Hóa ra recordslà nhanh nhất theo sau là hội tụ không có triệu chứng zipmapiter_tuples

Tôi sẽ sử dụng một thư viện simple_benchmarksmà tôi nhận được từ bài đăng này

from simple_benchmark import BenchmarkBuilder
b = BenchmarkBuilder()

import pandas as pd
import numpy as np

def tuple_comp(df): return [tuple(x) for x in df.to_numpy()]
def iter_namedtuples(df): return list(df.itertuples(index=False))
def iter_tuples(df): return list(df.itertuples(index=False, name=None))
def records(df): return df.to_records(index=False).tolist()
def zipmap(df): return list(zip(*map(df.get, df)))

funcs = [tuple_comp, iter_namedtuples, iter_tuples, records, zipmap]
for func in funcs:
    b.add_function()(func)

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

@b.add_arguments('Rows in DataFrame')
def argument_provider():
    for n in (10 ** (np.arange(4, 11) / 2)).astype(int):
        yield n, creator(n)

r = b.run()

Kiểm tra kết quả

r.to_pandas_dataframe().pipe(lambda d: d.div(d.min(1), 0))

        tuple_comp  iter_namedtuples  iter_tuples   records    zipmap
100       2.905662          6.626308     3.450741  1.469471  1.000000
316       4.612692          4.814433     2.375874  1.096352  1.000000
1000      6.513121          4.106426     1.958293  1.000000  1.316303
3162      8.446138          4.082161     1.808339  1.000000  1.533605
10000     8.424483          3.621461     1.651831  1.000000  1.558592
31622     7.813803          3.386592     1.586483  1.000000  1.515478
100000    7.050572          3.162426     1.499977  1.000000  1.480131

r.plot()

nhập mô tả hình ảnh ở đây


12

Dưới đây là một cách tiếp cận vector hóa (giả sử các dataframe, data_setđược định nghĩa là dfthay vì) mà trả về một listsố tuplesnhư hình:

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

sản xuất:

[(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)]

Ý tưởng đặt cột datetime làm trục chỉ mục là để hỗ trợ chuyển đổi Timestampgiá trị thành datetime.datetimeđịnh dạng tương ứng của nó bằng cách sử dụng convert_datetime64đối số trong DF.to_recordsđó làm như vậy choDateTimeIndex dữ liệu.

Điều này trả về một recarraycái mà sau đó có thể được thực hiện để trả về listviệc sử dụng.tolist


Giải pháp tổng quát hơn tùy thuộc vào trường hợp sử dụng sẽ là:

df.to_records().tolist()                              # Supply index=False to exclude index

10

Cách hiệu quả và dễ dàng nhất:

list(data_set.to_records())

Bạn có thể lọc các cột bạn cần trước cuộc gọi này.


1
Tôi nghĩ rằng 'index = false' nên được đưa ra làm đối số cho to_records (). Do đó, danh sách (data_set.to_records (index = false))
user3415167

8

Câu trả lời này không thêm bất kỳ câu trả lời nào chưa được thảo luận, nhưng đây là một số kết quả tốc độ. Tôi nghĩ rằng điều này sẽ giải quyết các câu hỏi đưa ra trong các ý kiến. Tất cả những thứ này trông giống như chúng là O (n) , dựa trên ba giá trị này.

TL; DR : tuples = list(df.itertuples(index=False, name=None))tuples = list(zip(*[df[c].values.tolist() for c in df]))được buộc nhanh nhất.

Tôi đã làm một bài kiểm tra tốc độ nhanh về kết quả cho ba gợi ý ở đây:

  1. Câu trả lời zip từ @pirsquared: tuples = list(zip(*[df[c].values.tolist() for c in df]))
  2. Câu trả lời được chấp nhận từ @ wes-mckinney: tuples = [tuple(x) for x in df.values]
  3. Câu trả lời của itertuples từ @ksindi với name=Nonegợi ý từ @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)})

Kích thước nhỏ:

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))

Cung cấp:

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)

Lớn hơn:

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))

Cung cấp:

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)

Nhiều kiên nhẫn như tôi có:

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))

Cung cấp:

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)

Phiên bản zip và phiên bản itertuples nằm trong khoảng tin cậy lẫn nhau. Tôi nghi ngờ rằng họ đang làm điều tương tự dưới mui xe.

Những bài kiểm tra tốc độ có lẽ không liên quan. Đẩy các giới hạn của bộ nhớ máy tính của tôi không mất nhiều thời gian và bạn thực sự không nên làm điều này trên một tập dữ liệu lớn. Làm việc với những bộ dữ liệu đó sau khi làm điều này sẽ thực sự không hiệu quả. Nó không có khả năng là một nút cổ chai lớn trong mã của bạn, vì vậy chỉ cần sử dụng phiên bản mà bạn nghĩ là dễ đọc nhất.


Tôi cập nhật bài viết cũ của tôi. Bây giờ tôi đã sử dụng [*zip(*map(df.get, df))]. Dù sao, nghĩ rằng bạn sẽ thấy nó thú vị.
piRSquared

@piRSquared Oooh. Tôi thích cốt truyện đẹp. Tôi đoán nó trông giống như O (n) .
TC Proctor

2
#try this one:

tuples = list(zip(data_set["data_date"], data_set["data_1"],data_set["data_2"]))
print (tuples)

2

Thay đổi danh sách khung dữ liệu thành một danh sách các bộ dữ liệu.

df = pd.DataFrame({'col1': [1, 2, 3], 'col2': [4, 5, 6]})
print(df)
OUTPUT
   col1  col2
0     1     4
1     2     5
2     3     6

records = df.to_records(index=False)
result = list(records)
print(result)
OUTPUT
[(1, 4), (2, 5), (3, 6)]

1
Vui lòng không chỉ đăng mã dưới dạng câu trả lời mà còn cung cấp giải thích về mã của bạn làm gì và cách giải quyết vấn đề của câu hỏi. Câu trả lời với một lời giải thích thường có chất lượng cao hơn, và có nhiều khả năng thu hút upvote.
Mark Rotteveel

1

Cách pythonic hơn:

df = data_set[['data_date', 'data_1', 'data_2']]
map(tuple,df.values)

Cách pythonic hơn: Thực tế hoàn toàn ngược lại. map()nổi tiếng là unpythonic.
AMC
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.