通过一次附加一行来创建Pandas数据框

如何创建一个空的DataFrame,然后添加行,一个接一个?

我创建了一个空的DataFrame

df = pd.DataFrame(columns=('lib', 'qty1', 'qty2'))

然后我可以在末尾添加一个新行并填写一个字段:

df = df._set_value(index=len(df), col='qty1', value=10.0)

它一次只适用于一个字段。将新行添加到df的更好方法是什么?

2082702 次浏览

您可以使用pandas.concat()。有关详细信息和示例,请参阅合并、连接和连接

例如:

def append_row(df, row):return pd.concat([df,pd.DataFrame([row], columns=row.index)]).reset_index(drop=True)
df = pd.DataFrame(columns=('lib', 'qty1', 'qty2'))new_row = pd.Series({'lib':'A', 'qty1':1, 'qty2': 2})
df = append_row(df, new_row)

如果您可以预先获取数据帧的所有数据,那么有一种比附加到数据帧更快的方法:

  1. 创建一个字典列表,其中每个字典对应于一个输入数据行。
  2. 从这个列表中创建一个数据帧。

我有一个类似的任务,逐行附加到数据帧需要30分钟,并从几秒钟内完成的字典列表中创建一个数据帧。

rows_list = []for row in input_rows:
dict1 = {}# get input row in dictionary format# key = col_namedict1.update(blah..)
rows_list.append(dict1)
df = pd.DataFrame(rows_list)

有关有效的附加,请参阅放大设置9368360">如何为熊猫数据框添加额外的行放大设置

不存在键索引数据上通过loc/ix添加行。例如:

In [1]: se = pd.Series([1,2,3])
In [2]: seOut[2]:0    11    22    3dtype: int64
In [3]: se[5] = 5.
In [4]: seOut[4]:0    1.01    2.02    3.05    5.0dtype: float64

或:

In [1]: dfi = pd.DataFrame(np.arange(6).reshape(3,2),.....:                 columns=['A','B']).....:
In [2]: dfiOut[2]:A  B0  0  11  2  32  4  5
In [3]: dfi.loc[:,'C'] = dfi.loc[:,'A']
In [4]: dfiOut[4]:A  B  C0  0  1  01  2  3  22  4  5  4In [5]: dfi.loc[3] = 5
In [6]: dfiOut[6]:A  B  C0  0  1  01  2  3  22  4  5  43  5  5  5

您可以使用df.loc[i],其中索引i的行将是您在数据框中指定的行。

>>> import pandas as pd>>> from numpy.random import randint
>>> df = pd.DataFrame(columns=['lib', 'qty1', 'qty2'])>>> for i in range(5):>>>     df.loc[i] = ['name' + str(i)] + list(randint(10, size=2))
>>> dflib qty1 qty20  name0    3    31  name1    2    42  name2    2    83  name3    2    14  name4    9    6

如果您事先知道条目的数量,您应该通过提供索引来预分配空间(以不同答案的数据示例为例):

import pandas as pdimport numpy as np# we know we're gonna have 5 rows of datanumberOfRows = 5# create dataframedf = pd.DataFrame(index=np.arange(0, numberOfRows), columns=('lib', 'qty1', 'qty2') )
# now fill it up row by rowfor x in np.arange(0, numberOfRows):#loc or iloc both work here since the index is natural numbersdf.loc[x] = [np.random.randint(-1,1) for n in range(3)]In[23]: dfOut[23]:lib  qty1  qty20   -1    -1    -11    0     0     02   -1     0    -13    0    -1     04   -1     0     0

速度比较

In[30]: %timeit tryThis() # function wrapper for this answerIn[31]: %timeit tryOther() # function wrapper without index (see, for example, @fred)1000 loops, best of 3: 1.23 ms per loop100 loops, best of 3: 2.31 ms per loop

而且-从评论中-大小为6000,速度差异变得更大:

增加数组的大小(12)和行数(500)速度差异更惊人:313ms vs 2.29s

mycolumns = ['A', 'B']df = pd.DataFrame(columns=mycolumns)rows = [[1,2],[3,4],[5,6]]for row in rows:df.loc[len(df)] = row

您可以使用ignore_index选项将单行追加为字典。

>>> f = pandas.DataFrame(data = {'Animal':['cow','horse'], 'Color':['blue', 'red']})>>> fAnimal Color0    cow  blue1  horse   red>>> f.append({'Animal':'mouse', 'Color':'black'}, ignore_index=True)Animal  Color0    cow   blue1  horse    red2  mouse  black

这不是对OP问题的回答,而是一个玩具例子来说明我发现非常有用的ShikharDua的回答

虽然这个片段很简单,但在实际数据中,我有1000行和许多列,我希望能够按不同的列分组,然后对多个目标列执行下面的统计。因此,有一种可靠的方法来一次构建一行数据帧是一个很大的方便。谢谢你ShikharDua!

import pandas as pd
BaseData = pd.DataFrame({ 'Customer' : ['Acme','Mega','Acme','Acme','Mega','Acme'],'Territory'  : ['West','East','South','West','East','South'],'Product'  : ['Econ','Luxe','Econ','Std','Std','Econ']})BaseData
columns = ['Customer','Num Unique Products', 'List Unique Products']
rows_list=[]for name, group in BaseData.groupby('Customer'):RecordtoAdd={} #initialise an empty dictRecordtoAdd.update({'Customer' : name}) #RecordtoAdd.update({'Num Unique Products' : len(pd.unique(group['Product']))})RecordtoAdd.update({'List Unique Products' : pd.unique(group['Product'])})
rows_list.append(RecordtoAdd)
AnalysedData = pd.DataFrame(rows_list)
print('Base Data : \n',BaseData,'\n\n Analysed Data : \n',AnalysedData)

创建一个新记录(数据帧)并添加到old_data_frame

传递和对应的名称列表以创建new_record(data_frame):

new_record = pd.DataFrame([[0, 'abcd', 0, 1, 123]], columns=['a', 'b', 'c', 'd', 'e'])
old_data_frame = pd.concat([old_data_frame, new_record])

另一种方法(可能不是很有效):

# add a rowdef add_row(df, row):colnames = list(df.columns)ncol = len(colnames)assert ncol == len(row), "Length of row must be the same as width of DataFrame: %s" % rowreturn df.append(pd.DataFrame([row], columns=colnames))

您还可以像这样增强DataFrame类:

import pandas as pddef add_row(self, row):self.loc[len(self.index)] = rowpd.DataFrame.add_row = add_row

为了Pythonic的方式:

res = pd.DataFrame(columns=('lib', 'qty1', 'qty2'))res = res.append([{'qty1':10.0}], ignore_index=True)print(res.head())
lib  qty1  qty20  NaN  10.0   NaN

让它变得简单。通过将列表作为输入,该列表将作为行附加到数据帧中:

import pandas as pdres = pd.DataFrame(columns=('lib', 'qty1', 'qty2'))for i in range(5):res_list = list(map(int, input().split()))res = res.append(pd.Series(res_list, index=['lib', 'qty1', 'qty2']), ignore_index=True)

您还可以建立列表列表并将其转换为数据框-

import pandas as pd
columns = ['i','double','square']rows = []
for i in range(6):row = [i, i*2, i*i]rows.append(row)
df = pd.DataFrame(rows, columns=columns)

给予

i   double  square0   0   0   01   1   2   12   2   4   43   3   6   94   4   8   165   5   10  25

这将负责将项目添加到空DataFrame。问题是第一个索引的df.index.max() == nan

df = pd.DataFrame(columns=['timeMS', 'accelX', 'accelY', 'accelZ', 'gyroX', 'gyroY', 'gyroZ'])
df.loc[0 if math.isnan(df.index.max()) else df.index.max() + 1] = [x for x in range(7)]

在向数据框添加大量行的情况下,我对在性能感兴趣。所以我尝试了四种最流行的方法并检查了它们的速度。

性能

  1. 使用. append(NPE的回答
  2. 使用. loc(弗雷德的回答
  3. 使用. loc预分配(FooBar的回答
  4. 使用字典并在最后创建DataFrame(ShikharDua的回答

运行时结果(以秒为单位):

方法1000行5000行10 000行
. append0.693.396.78
. loc没有prealloc0.743.908.35
. loc与prealloc0.242.588.70
字典0.0120.0460.084

所以我通过字典为自己使用加法。


代码:

import pandas as pdimport numpy as npimport time
del df1, df2, df3, df4numOfRows = 1000# appendstartTime = time.perf_counter()df1 = pd.DataFrame(np.random.randint(100, size=(5,5)), columns=['A', 'B', 'C', 'D', 'E'])for i in range( 1,numOfRows-4):df1 = df1.append( dict( (a,np.random.randint(100)) for a in ['A','B','C','D','E']), ignore_index=True)print('Elapsed time: {:6.3f} seconds for {:d} rows'.format(time.perf_counter() - startTime, numOfRows))print(df1.shape)
# .loc w/o preallocstartTime = time.perf_counter()df2 = pd.DataFrame(np.random.randint(100, size=(5,5)), columns=['A', 'B', 'C', 'D', 'E'])for i in range( 1,numOfRows):df2.loc[i]  = np.random.randint(100, size=(1,5))[0]print('Elapsed time: {:6.3f} seconds for {:d} rows'.format(time.perf_counter() - startTime, numOfRows))print(df2.shape)
# .loc with preallocdf3 = pd.DataFrame(index=np.arange(0, numOfRows), columns=['A', 'B', 'C', 'D', 'E'] )startTime = time.perf_counter()for i in range( 1,numOfRows):df3.loc[i]  = np.random.randint(100, size=(1,5))[0]print('Elapsed time: {:6.3f} seconds for {:d} rows'.format(time.perf_counter() - startTime, numOfRows))print(df3.shape)
# dictstartTime = time.perf_counter()row_list = []for i in range (0,5):row_list.append(dict( (a,np.random.randint(100)) for a in ['A','B','C','D','E']))for i in range( 1,numOfRows-4):dict1 = dict( (a,np.random.randint(100)) for a in ['A','B','C','D','E'])row_list.append(dict1)
df4 = pd.DataFrame(row_list, columns=['A','B','C','D','E'])print('Elapsed time: {:6.3f} seconds for {:d} rows'.format(time.perf_counter() - startTime, numOfRows))print(df4.shape)

P. S.:我相信我的实现并不完美,也许可以做一些优化。

我想出了一个简单而美好的方法:

>>> dfA  B  Cone  1  2  3>>> df.loc["two"] = [4,5,6]>>> dfA  B  Cone  1  2  3two  4  5  6

注意注释中的性能警告。

以下是在PandasDataFrame中添加/追加一行的方法:

def add_row(df, row):df.loc[-1] = rowdf.index = df.index + 1return df.sort_index()
add_row(df, [1,2,3])

它可用于在空的或填充的Pandas DataFrame中插入/追加一行。

我们经常看到构造df.loc[subscript] = …分配给一个DataFrame行。Mikhail_Sam公布的基准包含此构造以及使用最后判断并创建DataFrame的方法。他发现后者是迄今为止最快的。

但是如果我们将代码中的df3.loc[i] = …(使用预分配的DataFrame)替换为df3.values[i] = …,结果会发生显着变化,因为该方法的执行类似于使用字典的方法。因此我们应该更经常地考虑df.values[subscript] = …的使用。然而请注意,.values采用从零开始的下标,这可能与DataFrame.index.

您可以使用生成器对象来创建Dataframe,这将比列表更具内存效率。

num = 10
# Generator function to generate generator objectdef numgen_func(num):for i in range(num):yield ('name_{}'.format(i), (i*i), (i*i*i))
# Generator expression to generate generator object (Only once data get populated, can not be re used)numgen_expression = (('name_{}'.format(i), (i*i), (i*i*i)) for i in range(num) )
df = pd.DataFrame(data=numgen_func(num), columns=('lib', 'qty1', 'qty2'))

要将原始添加到现有的DataFrame,您可以使用append方法。

df = df.append([{ 'lib': "name_20", 'qty1': 20, 'qty2': 400  }])

ShikharDua的回答(基于行)中的字典列表不同,我们还可以将我们的表表示为列表字典(基于列),其中每个列表按行顺序存储一列,假设我们事先知道我们的列。最后,我们构造了我们的DataFrame一次

在这两种情况下,字典键总是列名。行顺序隐式存储为列表中的顺序。对于c列和n行,这使用一个c列表的字典,而一个n字典的列表。字典列表方法让每个字典冗余存储所有键,并需要为每一行创建一个新字典。这里我们只附加到列表,总体上时间复杂度相同(向列表和字典添加条目都是摊销常数时间),但由于操作简单,开销可能更少。

# Current datadata = {"Animal":["cow", "horse"], "Color":["blue", "red"]}
# Adding a new row (be careful to ensure every column gets another value)data["Animal"].append("mouse")data["Color"].append("black")
# At the end, construct our DataFramedf = pd.DataFrame(data)#   Animal  Color# 0    cow   blue# 1  horse    red# 2  mouse  black

pandas.DataFrame.append

DataFrame.append(自己,其他,ignore_index=False,verify_integrity=False,排序=False)→'DataFrame'

代码

df = pd.DataFrame([[1, 2], [3, 4]], columns=list('AB'))df2 = pd.DataFrame([[5, 6], [7, 8]], columns=list('AB'))df.append(df2)

ignore_index设置为True:

df.append(df2, ignore_index=True)

如果您想在末尾添加一行,请将其附加为列表:

valuestoappend = [va1, val2, val3]res = res.append(pd.Series(valuestoappend, index = ['lib', 'qty1', 'qty2']), ignore_index = True)

在添加行之前,我们必须将数据框转换为字典。在那里,您可以将键视为数据框中的列,列的值再次存储在字典中,但在那里,每列的键是数据框中的索引号。

这个想法让我写了下面的代码。

df2 = df.to_dict()values = ["s_101", "hyderabad", 10, 20, 16, 13, 15, 12, 12, 13, 25, 26, 25, 27, "good", "bad"] # This is the total row that we are going to addi = 0for x in df.columns:   # Here df.columns gives us the main dictionary keydf2[x][101] = values[i]   # Here the 101 is our index number. It is also the key of the sub dictionaryi += 1

您可以为此连接两个DataFrame。我基本上遇到了这个问题,以使用字符索引(而不是数字)向现有DataFrame添加新行。

所以,我在一个管道()中输入新行的数据,并在列表中输入索引。

new_dict = {put input for new row here}new_list = [put your index here]
new_df = pd.DataFrame(data=new_dict, index=new_list)
df = pd.concat([existing_df, new_df])

所有你需要的是loc[df.shape[0]]loc[len(df)]


# Assuming your df has 4 columns (str, int, str, bool)df.loc[df.shape[0]] = ['col1Value', 100, 'col3Value', False]

df.loc[len(df)] = ['col1Value', 100, 'col3Value', False]
initial_data = {'lib': np.array([1,2,3,4]), 'qty1': [1,2,3,4], 'qty2': [1,2,3,4]}
df = pd.DataFrame(initial_data)
df
lib    qty1    qty20    1    1    11    2    2    22    3    3    33    4    4    4
val_1 = [10]val_2 = [14]val_3 = [20]
df.append(pd.DataFrame({'lib': val_1, 'qty1': val_2, 'qty2': val_3}))
lib    qty1    qty20    1    1    11    2    2    22    3    3    33    4    4    40    10    14    20

您可以使用进行循环遍历值或添加值数组。

val_1 = [10, 11, 12, 13]val_2 = [14, 15, 16, 17]val_3 = [20, 21, 22, 43]
df.append(pd.DataFrame({'lib': val_1, 'qty1': val_2, 'qty2': val_3}))
lib    qty1    qty20    1    1    11    2    2    22    3    3    33    4    4    40    10    14    201    11    15    212    12    16    223    13    17    43

永远不要增长一个DataFrame!

是的,人们已经解释过你永远不应该增长一个DataFrame,你应该将你的数据附加到一个列表中,并在最后将其转换为一个DataFrame。但是你明白为什么吗?

以下是最重要的原因,摘自我的帖子这里

  1. 它总是更便宜/更快地附加到列表并一次性创建一个DataFrame。
  2. 列表占用更少的内存,并且是一种更轻的数据结构,可以使用、追加和删除。
  3. dtypes会自动为您的数据推断出来。另一方面,创建NaN的空帧会自动使它们成为object,这很糟糕。
  4. 索引会自动为您创建,而不必注意将正确的索引分配给要附加的行。

这是正确的方式™积累您的数据

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

这些选择太可怕了

  1. #0或#1在循环中

    appendconcat并不是天生的坏孤立地当您在循环中迭代调用它们时,问题就开始了-这导致二次内存使用。

    # Creates empty DataFrame and appendsdf = 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)# This is equally bad:# df = pd.concat(#       [df, pd.Series({'A': i, 'B': b, 'C': c})],#       ignore_index=True)
  2. NaN的空数据帧

    永远不要创建NaN的DataFrame,因为列是用object(慢,不可矢量化的dtype)。

    # Creates DataFrame of NaNs and overwrites values.df = pd.DataFrame(columns=['A', 'B', 'C'], index=range(5))for a, b, c in some_function_that_yields_data():df.loc[len(df)] = [a, b, c]

证据就在布丁里

对这些方法进行计时是查看它们在内存和效用方面有多大差异的最快方法。

在此处输入图片描述

基准代码以供参考。


正是这样的帖子提醒了我为什么成为这个社区的一员。人们明白教导人们用代码得到正确答案的重要性,而不是用错误代码得到正确答案的重要性。现在你可能会争辩说,如果你只向你的DataFrame添加一行,使用locappend不是问题。然而,人们经常在这个问题上添加不仅仅是一行-通常要求是使用来自函数的数据在循环中迭代地添加一行(见相关问题)。在这种情况下,重要的是要理解迭代地增长DataFrame不是一个好主意。

如果Dataframe中的所有数据都具有相同的dtype,您可能会使用NumPy数组。您可以将行直接写入预定义的数组,并在最后将其转换为数据帧。它似乎比转换一个DICTS列表更快。

import pandas as pdimport numpy as npfrom string import ascii_uppercase
startTime = time.perf_counter()numcols, numrows = 5, 10000npdf = np.ones((numrows, numcols))for row in range(numrows):npdf[row, 0:] = np.random.randint(0, 100, (1, numcols))df5 = pd.DataFrame(npdf, columns=list(ascii_uppercase[:numcols]))print('Elapsed time: {:6.3f} seconds for {:d} rows'.format(time.perf_counter() - startTime, numOfRows))print(df5.shape)

如果你有一个数据帧df并且想要将列表new_list作为新行添加到df,你可以简单地执行以下操作:

df.loc[len(df)] = new_list

如果要在数据帧df下添加新的数据帧new_df,则可以使用:

df.append(new_df)

如果您总是想在末尾添加新行,请使用:

df.loc[len(df)] = ['name5', 9, 0]

此代码片段使用字典列表来更新数据帧。它添加到Mikhail_Sam7496530">ShikharDua的和Mikhail_Sam答案。

import pandas as pdcolour = ["red", "big", "tasty"]fruits = ["apple", "banana", "cherry"]dict1={}feat_list=[]for x in colour:for y in fruits:#         print(x, y)dict1 = dict([('x',x),('y',y)])#         print(f'dict 1 {dict1}')feat_list.append(dict1)#         print(f'feat_list {feat_list}')feat_df=pd.DataFrame(feat_list)feat_df.to_csv('feat1.csv')