如何创建一个空的 DataFrame
,然后一一添加行?
我创建了一个空的 DataFrame
:
df = pd.DataFrame(columns=('lib', 'qty1', 'qty2'))
然后我可以在最后添加一个新行并用以下内容填充单个字段:
df = df._set_value(index=len(df), col='qty1', value=10.0)
它一次只适用于一个领域。向 df
添加新行的更好方法是什么?
您可以使用 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))
>>> df
lib qty1 qty2
0 name0 3 3
1 name1 2 4
2 name2 2 8
3 name3 2 1
4 name4 9 6
如果您可以预先获取数据框的所有数据,则有一种比附加到数据框更快的方法:
创建一个字典列表,其中每个字典对应一个输入数据行。从此列表创建一个数据框。
我有一个类似的任务,逐行附加到数据帧需要 30 分钟,并从几秒钟内完成的字典列表创建一个数据帧。
rows_list = []
for row in input_rows:
dict1 = {}
# get input row in dictionary format
# key = col_name
dict1.update(blah..)
rows_list.append(dict1)
df = pd.DataFrame(rows_list)
It is worth noting however, that concat (and therefore append) makes a full copy of the data, and that constantly reusing this function can create a significant performance hit. If you need to use the operation over several datasets, use a list comprehension.
(pandas.pydata.org/pandas-docs/stable/…)
在向数据框添加大量行的情况下,我对性能感兴趣。所以我尝试了四种最流行的方法并检查了它们的速度。
表现
使用 .append(NPE 的回答) 使用 .loc(fred 的回答) 使用 .loc 进行预分配(FooBar 的回答) 使用 dict 并最终创建 DataFrame(ShikharDua 的回答)
运行时结果(以秒为单位):
接近 1000 行 5000 行 10 000 行 .append 0.69 3.39 6.78 .loc without prealloc 0.74 3.90 8.35 .loc with prealloc 0.24 2.58 8.70 dict 0.012 0.046 0.084
所以我自己通过字典使用加法。
代码:
import pandas as pd
import numpy as np
import time
del df1, df2, df3, df4
numOfRows = 1000
# append
startTime = 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 prealloc
startTime = 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 prealloc
df3 = 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)
# dict
startTime = 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)
PS:我相信我的实现并不完美,也许可以做一些优化。
df2.index.max()
用于 .loc
会不必要地增加计算复杂性。简单的 df2.loc[i] = ...
就可以了。对我来说,它将时间从 10 秒减少到 8.64 秒
for i in range (0,5):
和 for i in range( 1,numOfRows-4):
背后的基本原理是什么?
您可以使用 pandas.concat()
或 DataFrame.append()
。有关详细信息和示例,请参阅 Merge, join, and concatenate。
.loc
机制,特别是如果您小心的话。
DataFrame.append()
,你必须首先确保你的行数据也是一个 DataFrame,而不是一个列表。
永远不要增长 DataFrame!
是的,人们已经解释过你不应该增长一个 DataFrame,你应该将你的数据附加到一个列表中,并在最后将它转换为一个 DataFrame。但是你明白为什么吗?
以下是最重要的原因,摘自我的帖子 here。
追加到列表并一次性创建 DataFrame 总是更便宜/更快。列表占用的内存更少,并且是一种更轻便的数据结构,可以使用、追加和删除。 dtypes 会自动为您的数据推断。另一方面,创建一个空的 NaN 框架会自动使它们成为对象,这很糟糕。系统会自动为您创建一个索引,而您不必小心为要附加的行分配正确的索引。
这是积累数据的正确方式™
data = []
for a, b, c in some_function_that_yields_data():
data.append([a, b, c])
df = pd.DataFrame(data, columns=['A', 'B', 'C'])
这些选项太可怕了
循环中的 append 或 concat append 和 concat 本身并不是孤立的。当您在循环中迭代地调用它们时,问题就开始了——这会导致二次内存使用。 # 创建空 DataFrame 并在 some_function_that_yields_data() 中为 a, b, c 添加 df = pd.DataFrame(columns=['A', 'B', 'C']): df = df.append({'A': i, 'B': b, 'C': c}, ignore_index=True) # 这同样糟糕:# df = pd.concat( # [df, pd.Series({'A': i, 'B' : b, 'C': c})], #ignore_index=True) NaN 的空 DataFrame 永远不要创建 NaN 的 DataFrame,因为列是用 object 初始化的(慢的,不可矢量化的 dtype)。 # 创建 NaN 的 DataFrame 并覆盖值。 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]
证据就在布丁里
对这些方法进行计时是查看它们在内存和效用方面有多大差异的最快方法。
https://i.stack.imgur.com/sGIV6.png
Benchmarking code for reference.
像这样的帖子提醒我为什么我是这个社区的一员。人们明白教人们用正确代码得到正确答案的重要性,而不是用错误代码得到正确答案的重要性。现在您可能会争辩说,如果您只是向 DataFrame 添加一行,那么使用 loc
或 append
不是问题。然而,人们通常希望这个问题不仅仅是添加一个行 - 通常要求是使用来自函数的数据在循环内迭代地添加一行(见related question)。在这种情况下,重要的是要了解迭代增长 DataFrame 不是一个好主意。
如果您事先知道条目数,则应通过提供索引来预先分配空间(以不同答案中的数据示例为例):
import pandas as pd
import numpy as np
# we know we're gonna have 5 rows of data
numberOfRows = 5
# create dataframe
df = pd.DataFrame(index=np.arange(0, numberOfRows), columns=('lib', 'qty1', 'qty2') )
# now fill it up row by row
for x in np.arange(0, numberOfRows):
#loc or iloc both work here since the index is natural numbers
df.loc[x] = [np.random.randint(-1,1) for n in range(3)]
In[23]: df
Out[23]:
lib qty1 qty2
0 -1 -1 -1
1 0 0 0
2 -1 0 -1
3 0 -1 0
4 -1 0 0
速度比较
In[30]: %timeit tryThis() # function wrapper for this answer
In[31]: %timeit tryOther() # function wrapper without index (see, for example, @fred)
1000 loops, best of 3: 1.23 ms per loop
100 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
len(df.index)
。
您可以使用 ignore_index
选项将单行附加为字典。
>>> f = pandas.DataFrame(data = {'Animal':['cow','horse'], 'Color':['blue', 'red']})
>>> f
Animal Color
0 cow blue
1 horse red
>>> f.append({'Animal':'mouse', 'Color':'black'}, ignore_index=True)
Animal Color
0 cow blue
1 horse red
2 mouse black
f.append(<stuff>)
创建了一个新对象,而不是简单地追加到当前对象,因此如果您尝试追加到脚本中的数据框,您需要说 f = f.append(<stuff>)
如需高效追加,请参阅 How to add an extra row to a pandas dataframe 和 Setting With Enlargement。
通过 loc/ix
在不存在键索引数据上添加行。例如:
In [1]: se = pd.Series([1,2,3])
In [2]: se
Out[2]:
0 1
1 2
2 3
dtype: int64
In [3]: se[5] = 5.
In [4]: se
Out[4]:
0 1.0
1 2.0
2 3.0
5 5.0
dtype: float64
或者:
In [1]: dfi = pd.DataFrame(np.arange(6).reshape(3,2),
.....: columns=['A','B'])
.....:
In [2]: dfi
Out[2]:
A B
0 0 1
1 2 3
2 4 5
In [3]: dfi.loc[:,'C'] = dfi.loc[:,'A']
In [4]: dfi
Out[4]:
A B C
0 0 1 0
1 2 3 2
2 4 5 4
In [5]: dfi.loc[3] = 5
In [6]: dfi
Out[6]:
A B C
0 0 1 0
1 2 3 2
2 4 5 4
3 5 5 5
为了 Pythonic 方式:
res = pd.DataFrame(columns=('lib', 'qty1', 'qty2'))
res = res.append([{'qty1':10.0}], ignore_index=True)
print(res.head())
lib qty1 qty2
0 NaN 10.0 NaN
您还可以建立列表列表并将其转换为数据框 -
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 square 0 0 0 0 1 1 2 1 2 2 4 4 3 3 6 9 4 4 8 16 5 5 10 25
我想出了一个简单而好方法:
>>> df
A B C
one 1 2 3
>>> df.loc["two"] = [4,5,6]
>>> df
A B C
one 1 2 3
two 4 5 6
请注意评论中提到的性能警告。
如果您总是想在最后添加一个新行,请使用以下命令:
df.loc[len(df)] = ['name5', 9, 0]
df.reset_index()
可以解决此问题,但就目前而言,它实际上可能会覆盖现有行。
这不是 OP 问题的答案,而是说明 ShikharDua's answer 的玩具示例,我发现它非常有用。
虽然这个片段是微不足道的,但在实际数据中,我有 1,000 行和许多列,我希望能够按不同的列进行分组,然后对多个目标列执行下面的统计信息。因此,有一种可靠的方法来一次构建一行数据框是非常方便的。谢谢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 dict
RecordtoAdd.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)
您可以使用生成器对象来创建数据框,这将比列表更节省内存。
num = 10
# Generator function to generate generator object
def 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'))
要将 raw 添加到现有 DataFrame 中,您可以使用 append 方法。
df = df.append([{ 'lib': "name_20", 'qty1': 20, 'qty2': 400 }])
创建一个新记录(数据框)并添加到 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])
除了 ShikharDua's answer 中的字典列表之外,我们还可以将表表示为 列表字典,其中每个列表按行顺序存储一列,前提是我们事先知道列。 最后我们构建了一次 DataFrame。
对于 c 列和 n 行,这使用一个字典和 c 个列表,而不是一个列表和 n 个字典。 list-of-dictionaries 方法让每个字典存储所有键,并且需要为每一行创建一个新字典。这里我们只追加到列表,这是恒定的时间,理论上非常快。
# Current data
data = {"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 DataFrame
df = pd.DataFrame(data)
# Animal Color
# 0 cow blue
# 1 horse red
# 2 mouse black
以下是在 Pandas DataFrame
中添加/追加行的方法:
def add_row(df, row):
df.loc[-1] = row
df.index = df.index + 1
return df.sort_index()
add_row(df, [1,2,3])
它可用于在空的或填充的 Pandas DataFrame 中插入/追加一行。
如果要在末尾添加一行,请将其附加为列表:
valuestoappend = [va1, val2, val3]
res = res.append(pd.Series(valuestoappend, index = ['lib', 'qty1', 'qty2']), ignore_index = True)
另一种方法(可能不是很好):
# add a row
def 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" % row
return df.append(pd.DataFrame([row], columns=colnames))
您还可以像这样增强 DataFrame 类:
import pandas as pd
def add_row(self, row):
self.loc[len(self.index)] = row
pd.DataFrame.add_row = add_row
您只需要 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 qty2
0 1 1 1
1 2 2 2
2 3 3 3
3 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 qty2
0 1 1 1
1 2 2 2
2 3 3 3
3 4 4 4
0 10 14 20
您可以使用 for 循环遍历值或添加值数组。
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 qty2
0 1 1 1
1 2 2 2
2 3 3 3
3 4 4 4
0 10 14 20
1 11 15 21
2 12 16 22
3 13 17 43
您可以为此连接两个 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])
让它变得简单。通过将列表作为输入,该列表将作为一行附加到数据框中:
import pandas as pd
res = 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)
pandas.DataFrame.append
DataFrame.append(self, other, ignore_index=False, verify_integrity=False, sort=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)
如果您有一个数据框 df
并希望将 list new_list
作为新行添加到 df
,您只需执行以下操作:
df.loc[len(df)] = new_list
如果您想在数据框 df
下添加一个新的数据框 new_df
,那么您可以使用:
df.append(new_df)
我们经常看到构造 df.loc[subscript] = …
分配给一个 DataFrame 行。 Mikhail_Sam posted benchmarks 包含此构造以及使用 dict 并最终创建 DataFrame 的方法。他发现后者是迄今为止最快的。
但是如果我们用 df3.values[i] = …
替换他代码中的 df3.loc[i] = …
(带有预分配的 DataFrame),结果会发生显着变化,因为该方法的执行类似于使用 dict 的方法。所以我们应该更多地考虑使用 df.values[subscript] = …
。但是请注意,.values
采用从零开始的下标,这可能与 DataFrame.index 不同。
# .loc with prealloc
),另一个示例位于问题 I have to compare data from each row of a Pandas DataFrame with data from the rest of the rows, is there a way to speed up the computation? 及其接受的答案中。
在添加行之前,我们必须将数据框转换为字典。在那里,您可以将键视为数据框中的列,并且列的值再次存储在字典中,但每列的键是数据框中的索引号。
这个想法让我写了下面的代码。
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 add
i = 0
for x in df.columns: # Here df.columns gives us the main dictionary key
df2[x][101] = values[i] # Here the 101 is our index number. It is also the key of the sub dictionary
i += 1
如果 Dataframe 中的所有数据都具有相同的 dtype,则可以使用 NumPy 数组。您可以将行直接写入预定义的数组并在最后将其转换为数据帧。它似乎比转换字典列表还要快。
import pandas as pd
import numpy as np
from string import ascii_uppercase
startTime = time.perf_counter()
numcols, numrows = 5, 10000
npdf = 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)
此代码片段使用字典列表来更新数据框。它增加了 ShikharDua's 和 Mikhail_Sam's 的答案。
import pandas as pd
colour = ["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')
.loc
正在引用索引列,因此如果您使用的索引不是以 0 开头的连续整数序列(如您的示例中所示)的预先存在的 DataFrame,则.loc
将覆盖现有行,或插入行,或在索引中创建间隙。附加现有非零长度数据帧的更强大(但不是万无一失)的方法是:df.loc[df.index.max() + 1] = [randint(...
或按照@FooBar 的建议预填充索引。df.index.max()
为nan
。df.loc[0 if pd.isnull(df.index.max()) else df.index.max() + 1]