Tạo một DataFrame trống, sau đó điền nó?


461

Tôi đang bắt đầu từ các tài liệu DataFrame của gấu trúc ở đây: http://pandas.pydata.org/pandas-docs/urdy/dsintro.html

Tôi muốn lặp lại điền vào DataFrame bằng các giá trị trong loại tính toán theo chuỗi thời gian. Về cơ bản, tôi muốn khởi tạo DataFrame với các cột A, B và các dấu thời gian, tất cả 0 hoặc tất cả NaN.

Sau đó, tôi sẽ thêm các giá trị ban đầu và chuyển qua dữ liệu này để tính toán hàng mới từ hàng trước đó, giả sử row[A][t] = row[A][t-1]+1như vậy.

Tôi hiện đang sử dụng mã như dưới đây, nhưng tôi cảm thấy nó thật xấu xí và phải có cách trực tiếp để làm điều này với DataFrame hoặc nói chung là một cách tốt hơn. Lưu ý: Tôi đang sử dụng Python 2.7.

import datetime as dt
import pandas as pd
import scipy as s

if __name__ == '__main__':
    base = dt.datetime.today().date()
    dates = [ base - dt.timedelta(days=x) for x in range(0,10) ]
    dates.sort()

    valdict = {}
    symbols = ['A','B', 'C']
    for symb in symbols:
        valdict[symb] = pd.Series( s.zeros( len(dates)), dates )

    for thedate in dates:
        if thedate > dates[0]:
            for symb in valdict:
                valdict[symb][thedate] = 1+valdict[symb][thedate - dt.timedelta(days=1)]

    print valdict

6
Không bao giờ phát triển một DataFrame! Luôn luôn rẻ hơn khi thêm vào danh sách python và sau đó chuyển đổi nó thành DataFrame ở cuối, cả về bộ nhớ và hiệu suất.
cs95

@ cs95 Có gì khác biệt về chức năng giữa .appendtrong pd và nối thêm một danh sách? Tôi biết .appendtrong gấu trúc sao chép toàn bộ tập dữ liệu vào một đối tượng mới, liệu pythons có hoạt động khác không?
Lamma

@Lamma vui lòng tìm chi tiết trong câu trả lời của tôi dưới đây. Khi nối thêm vào df, DataFrame mới được tạo mỗi lần trong bộ nhớ thay vì sử dụng dữ liệu hiện có, điều này hoàn toàn lãng phí.
cs95

Câu trả lời:


330

Dưới đây là một vài gợi ý:

Sử dụng date_rangecho chỉ mục:

import datetime
import pandas as pd
import numpy as np

todays_date = datetime.datetime.now().date()
index = pd.date_range(todays_date-datetime.timedelta(10), periods=10, freq='D')

columns = ['A','B', 'C']

Lưu ý: chúng tôi có thể tạo một DataFrame trống (có NaNs) chỉ bằng cách viết:

df_ = pd.DataFrame(index=index, columns=columns)
df_ = df_.fillna(0) # with 0s rather than NaNs

Để thực hiện các loại tính toán này cho dữ liệu, hãy sử dụng một mảng gọn gàng:

data = np.array([np.arange(10)]*3).T

Do đó, chúng ta có thể tạo DataFrame:

In [10]: df = pd.DataFrame(data, index=index, columns=columns)

In [11]: df
Out[11]: 
            A  B  C
2012-11-29  0  0  0
2012-11-30  1  1  1
2012-12-01  2  2  2
2012-12-02  3  3  3
2012-12-03  4  4  4
2012-12-04  5  5  5
2012-12-05  6  6  6
2012-12-06  7  7  7
2012-12-07  8  8  8
2012-12-08  9  9  9

2
pd.date_range () không hoạt động đối với tôi. Tôi đã thử với DateRange (từ tự động hoàn thành của nhật thực), nhưng nó hoạt động với các chuỗi như định dạng ngày, phải không? Cách tiếp cận tổng thể hoạt động mặc dù (tôi đã thay đổi chỉ mục sang một cái gì đó khác).
Matthias Kauer

2
date_range là một chức năng của nhà máy để tạo các chỉ mục datetime và là một tính năng mới trong 0.8.0 , tôi chắc chắn sẽ khuyên bạn nên nâng cấp lên bản phát hành ổn định mới nhất (0.9.1) có nhiều sửa lỗi và các tính năng mới. :)
Andy Hayden

26
Theo kinh nghiệm của tôi, việc tạo khung dữ liệu có kích thước cần thiết chứa đầy NaN và sau đó điền vào các giá trị chậm hơn nhiều so với việc tạo khung dữ liệu với indexx 0kích thước ( columns = []) và gắn một cột trong mỗi lượt của một vòng lặp. Tôi có nghĩa là df[col_name] = pandas.Series([...])trong một vòng lặp lặp qua tên cột. Trong trường hợp trước, không chỉ việc cấp phát bộ nhớ mất thời gian mà việc thay thế NaN bằng các giá trị mới dường như cực kỳ chậm.
deeenes

5
@deeenes chắc chắn. câu trả lời này có lẽ sẽ làm cho nó rõ ràng hơn - bạn rất hiếm khi (nếu có) muốn tạo một Dataframe trống (của NaNs).
Andy Hayden

1
Theo câu trả lời này stackoverflow.com/a/30267881/2302569 Bạn cần gán kết quả của fillna hoặc vượt qua param inplace = True
JayJay

169

Nếu bạn chỉ muốn tạo một khung dữ liệu trống và lấp đầy nó với một số khung dữ liệu đến sau, hãy thử điều này:

newDF = pd.DataFrame() #creates a new dataframe that's empty
newDF = newDF.append(oldDF, ignore_index = True) # ignoring index is optional
# try printing some data from newDF
print newDF.head() #again optional 

Trong ví dụ này, tôi đang sử dụng tài liệu gấu trúc này để tạo khung dữ liệu mới và sau đó sử dụng chắp thêm để ghi vào newDF với dữ liệu từ oldDF.

Nếu tôi phải tiếp tục nối thêm dữ liệu mới vào newDF này từ nhiều hơn một oldDF, tôi chỉ sử dụng một vòng lặp for để lặp lại trên pandas.DataFrame.append ()


14
Xin lưu ý rằng append(và tương tự concat) sao chép toàn bộ dữ liệu vào một đối tượng mới mỗi lần, do đó, việc lặp lại và nối thêm có thể và sẽ gây ra một hiệu suất lớn. để biết thêm tham khảo: pandas.pydata.org/pandas-docs/stable/merging.html
MoustafaAAtta

4
@MoustafaAAtta Các lựa chọn thay thế để nối dữ liệu lặp vào dữ liệu là gì?
MysteryGuy

2
@MoustafaAAtta Có phải Fred trả lời trong bài này: stackoverflow.com/questions/10715965/ rèn tốt hơn về quan điểm này?
MysteryGuy

@MoustafaAAtta có lẽ bạn chỉ có thể nối các hàng vào một khung dữ liệu, nó vẫn sẽ tạo một đối tượng mới nhưng đối với các bộ dữ liệu nhỏ hơn, có thể hữu ích. pandas.pydata.org/pandas-docs/ sóng / user_guide / từ
geekidharsh

135

Đúng cách ™ để tạo một DataFrame

TLDR; (chỉ cần đọc văn bản in đậm)

Hầu hết các câu trả lời ở đây sẽ cho bạn biết cách tạo một DataFrame trống và điền nó, nhưng không ai sẽ nói với bạn rằng đó là một việc xấu phải làm.

Đây là lời khuyên của tôi: Đợi cho đến khi bạn chắc chắn rằng bạn có tất cả dữ liệu bạn cần để làm việc. Sử dụng danh sách để thu thập dữ liệu của bạn, sau đó khởi tạo DataFrame khi bạn sẵn sàng.

data = []
for a, b, c in some_function_that_yields_data():
    data.append([a, b, c])

df = pd.DataFrame(data, columns=['A', 'B', 'C'])

Việc thêm vào danh sách và tạo DataFrame trong một lần luôn rẻ hơn so với việc tạo một DataFrame trống (hoặc một trong các NaN) và nối lại nó nhiều lần. Danh sách cũng chiếm ít bộ nhớ hơn và là một cấu trúc dữ liệu nhẹ hơn nhiều để làm việc với , chắp thêm và xóa (nếu cần).

Ưu điểm khác của phương pháp này dtypeslà tự động được suy ra (thay vì gánobject cho tất cả chúng).

Ưu điểm cuối cùng là a RangeIndexđược tạo tự động cho dữ liệu của bạn , do đó, một điều ít phải lo lắng hơn (hãy xem người nghèo appendlocphương pháp bên dưới, bạn sẽ thấy các yếu tố trong cả hai yêu cầu xử lý chỉ mục một cách thích hợp).


Những điều bạn KHÔNG nên làm

appendhoặc concatbên trong một vòng lặp

Đây là sai lầm lớn nhất tôi từng thấy từ những người mới bắt đầu:

df = pd.DataFrame(columns=['A', 'B', 'C'])
for a, b, c in some_function_that_yields_data():
    df = df.append({'A': i, 'B': b, 'C': c}, ignore_index=True) # yuck
    # or similarly,
    # df = pd.concat([df, pd.Series({'A': i, 'B': b, 'C': c})], ignore_index=True)

Bộ nhớ được phân bổ lại cho mọi appendhoặc concathoạt động bạn có. Kết hợp điều này với một vòng lặp và bạn có một hoạt động phức tạp bậc hai . Từ df.appendtrang tài liệu :

Lặp đi lặp lại các hàng vào DataFrame có thể được tính toán chuyên sâu hơn so với một liên kết đơn. Một giải pháp tốt hơn là nối các hàng đó vào một danh sách và sau đó nối danh sách với DataFrame gốc cùng một lúc.

Một lỗi khác liên quan đến df.appendlà người dùng có xu hướng quên append không phải là một chức năng tại chỗ , do đó, kết quả phải được gán lại. Bạn cũng phải lo lắng về các dtypes:

df = pd.DataFrame(columns=['A', 'B', 'C'])
df = df.append({'A': 1, 'B': 12.3, 'C': 'xyz'}, ignore_index=True)

df.dtypes
A     object   # yuck!
B    float64
C     object
dtype: object

Đối phó với các cột đối tượng không bao giờ là một điều tốt, bởi vì gấu trúc không thể vector hóa các hoạt động trên các cột đó. Bạn sẽ cần phải làm điều này để sửa nó:

df.infer_objects().dtypes
A      int64
B    float64
C     object
dtype: object

loc bên trong một vòng lặp

Tôi cũng đã thấy locđược sử dụng để chắp thêm vào DataFrame được tạo trống:

df = pd.DataFrame(columns=['A', 'B', 'C'])
for a, b, c in some_function_that_yields_data():
    df.loc[len(df)] = [a, b, c]

Như trước đây, bạn chưa phân bổ trước số lượng bộ nhớ bạn cần mỗi lần, vì vậy bộ nhớ được tăng trưởng lại mỗi khi bạn tạo một hàng mới . Nó cũng tệ nhưappend , và thậm chí còn xấu xí hơn.

Khung dữ liệu trống của NaN

Và sau đó, sẽ tạo ra một DataFrame của NaN và tất cả các cảnh báo liên quan.

df = pd.DataFrame(columns=['A', 'B', 'C'], index=range(5))
df
     A    B    C
0  NaN  NaN  NaN
1  NaN  NaN  NaN
2  NaN  NaN  NaN
3  NaN  NaN  NaN
4  NaN  NaN  NaN

Nó tạo ra một DataFrame của các cột đối tượng, giống như các cột khác.

df.dtypes
A    object  # you DON'T want this
B    object
C    object
dtype: object

Áp dụng vẫn có tất cả các vấn đề như các phương pháp trên.

for i, (a, b, c) in enumerate(some_function_that_yields_data()):
    df.iloc[i] = [a, b, c]

Bằng chứng là trong pudding

Định thời gian cho các phương thức này là cách nhanh nhất để xem chúng khác nhau bao nhiêu về bộ nhớ và tiện ích của chúng.

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

Mã điểm chuẩn để tham khảo.


6
Danh sách nối thêm phải là cách tốt nhất cho loại câu hỏi này
YOBEN_S

9
Điều này cần phải được nâng cao hơn một triệu lần. Không bao giờ phát triển một khung dữ liệu!
Buggy

3
@ user3293236 Quá tệ, bạn phải bắt đầu từ dưới lên mỗi khi bạn trả lời một câu hỏi cũ;)
cs95

2
Đây là một trong những điều tôi ghét nhất. Nhiều lần bạn thấy 𝒄𝒐𝒓𝒓𝒆𝒄𝒕 chỉ ở đâu đó với vài phiếu và không bao giờ được chấp nhận. Tôi nhớ mã với = .𝙳𝚊𝚝𝚊𝙵𝚛𝚊𝚖𝚎 ([]) để tạo một khung dữ liệu gấu trúc trống. Nâng cao câu trả lời này. Giải thích tuyệt vời, @ cs95!
jonathan

1
Đây là nghĩa đen trong tài liệu. "Lặp đi lặp lại các hàng vào DataFrame có thể tính toán chuyên sâu hơn so với một nối đơn. Một giải pháp tốt hơn là nối các hàng đó vào danh sách và sau đó ghép tất cả danh sách với DataFrame gốc cùng một lúc." pandas.pydata.org/pandas-docs/version/0.21/generated/ mài
endolith

132

Khởi tạo khung trống với tên cột

import pandas as pd

col_names =  ['A', 'B', 'C']
my_df  = pd.DataFrame(columns = col_names)
my_df

Thêm bản ghi mới vào khung

my_df.loc[len(my_df)] = [2, 4, 5]

Bạn cũng có thể muốn vượt qua một từ điển:

my_dic = {'A':2, 'B':4, 'C':5}
my_df.loc[len(my_df)] = my_dic 

Nối một khung khác vào khung hiện tại của bạn

col_names =  ['A', 'B', 'C']
my_df2  = pd.DataFrame(columns = col_names)
my_df = my_df.append(my_df2)

Cân nhắc hiệu suất

Nếu bạn đang thêm các hàng bên trong một vòng lặp, hãy xem xét các vấn đề về hiệu suất. Đối với khoảng 1000 bản ghi đầu tiên, hiệu suất "my_df.loc" tốt hơn, nhưng nó dần trở nên chậm hơn bằng cách tăng số lượng bản ghi trong vòng lặp.

Nếu bạn có kế hoạch thực hiện các hoạt động bên trong một vòng lặp lớn (giả sử các bản ghi 10M‌ hoặc hơn), tốt hơn hết là bạn nên sử dụng hỗn hợp cả hai; điền vào một khung dữ liệu với iloc cho đến khi kích thước đạt khoảng 1000, sau đó nối nó vào khung dữ liệu ban đầu và làm trống khung dữ liệu tạm thời. Điều này sẽ tăng hiệu suất của bạn khoảng 10 lần.


my_df = my_df.append(my_df2)không làm việc cho tôi trừ khi tôi chỉ định ignore_index=True.
Nasif Imtiaz Ohi

0

Giả sử một khung dữ liệu với 19 hàng

index=range(0,19)
index

columns=['A']
test = pd.DataFrame(index=index, columns=columns)

Giữ cột A là hằng số

test['A']=10

Giữ cột b là một biến được cho bởi một vòng lặp

for x in range(0,19):
    test.loc[[x], 'b'] = pd.Series([x], index = [x])

Bạn có thể thay thế x đầu tiên pd.Series([x], index = [x])bằng bất kỳ giá trị nào

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.