Python—向dataframe添加行的有效方法
从这一点和其他方面来看,似乎不建议使用Python—向dataframe添加行的有效方法,python,pandas,numpy,dataframe,Python,Pandas,Numpy,Dataframe,从这一点和其他方面来看,似乎不建议使用concat或append来构建熊猫数据帧,因为每次都会重新复制整个数据帧 我的项目涉及每30秒检索少量数据。这可能会运行3天的周末,因此有人很容易期望一次创建一行超过8000行。向该数据框添加行的最有效方法是什么?您需要将问题分为两部分: 每30秒有效地接受数据(收集数据) 收集数据后处理数据 如果您的数据是关键的(也就是说,您不能承担丢失数据的代价),请将其发送到队列,然后从队列中批量读取数据 队列将提供可靠的(保证的)接受,并且您的数据不会丢失 您可以
concat
或append
来构建熊猫数据帧,因为每次都会重新复制整个数据帧
我的项目涉及每30秒检索少量数据。这可能会运行3天的周末,因此有人很容易期望一次创建一行超过8000行。向该数据框添加行的最有效方法是什么?您需要将问题分为两部分:
底线-拆分应用程序的收集和分析部分。在此处编辑所选答案,因为它完全错了。下面解释了为什么不应使用放大设置“放大设置”实际上比追加更糟糕。 tl;dr这里的问题是,没有有效的方法使用数据帧来实现这一点,因此如果您需要速度,您应该使用另一种数据结构来代替。查看其他答案以获得更好的解决方案 更多关于放大设置的信息 您可以在不存在的索引上使用
loc
将行添加到数据帧中,但这也会执行所有数据的复制(请参阅)。下面是它的外观,从:
对于类似所描述的用例,使用放大设置实际上比追加所需的时间长50%
:
使用append()
,8000行耗时6.59秒(每行0.8毫秒)
使用.loc()
,8000行耗时10秒(每行1.25毫秒)
那么更长的数据帧呢?
与面向数据的代码中的所有评测一样,YMMV和您应该针对您的用例进行测试。append
和“放大设置”的写入时拷贝行为的一个特点是,随着数据帧的增大,它会变得越来越慢:
%%timeit df = pd.DataFrame(columns=["A", "B", "C"]); new_row = pd.Series({"A": 4, "B": 4, "C": 4})
for i in range(16000):
df.loc[i] = new_row
# 23.7 s ± 286 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
用这种方法构建一个16k行的数据帧需要比8k行长2.3倍。我使用了这个答案的
df.loc[I]=[new_data]
建议,但我有>500000行,速度非常慢
虽然给出的答案对OP的问题很有帮助,但我发现在处理前面的大量行(而不是OP描述的欺骗)时,使用csvwriter向内存中的CSV对象添加数据,然后最后使用pandas.read_CSV(CSV)
生成所需的数据帧输出更有效
from io import BytesIO
from csv import writer
import pandas as pd
output = BytesIO()
csv_writer = writer(output)
for row in iterable_object:
csv_writer.writerow(row)
output.seek(0) # we need to get back to the start of the BytesIO
df = pd.read_csv(output)
return df
这样,大约500000行的速度提高了1000倍,随着行数的增加,速度的提高只会越来越大(df.loc[1]=[data]
相对来说会慢得多)
希望这能帮助那些在处理比OP多的行时需要效率的人。假设您的数据帧是按顺序索引的,您可以: 首先检查创建新行的下一个索引值:
myindex = df.shape[0]+1
然后使用“at”写入每个所需的列
df.at[myindex,'A']=val1
df.at[myindex,'B']=val2
df.at[myindex,'C']=val3
圣丹斯的答案在用法上可能是正确的,但基准测试是错误的。
正如moobie正确指出的那样,本例中已经存在索引3,这使得访问方式比使用不存在的索引更快。看看这个:
%%timeit
test = pd.DataFrame({"A": [1,2,3], "B": [1,2,3], "C": [1,2,3]})
for i in range(0,1000):
testrow = pd.DataFrame([0,0,0])
pd.concat([test[:1], testrow, test[1:]])
每个回路2.15 s±88 ms(7次运行的平均值±标准偏差,每个回路1次)
每个回路972 ms±14.4 ms(7次运行的平均值±标准偏差,每个回路1次)
每个回路1.13 s±46 ms(7次运行的平均值±标准偏差,每个回路1次)
当然,这是纯合成的,我承认我并不期待这些结果,但似乎在不存在索引的情况下,.loc
和.append
的表现非常相似。把这个留在这里。汤姆·哈维的反应很好。不过,我想根据这一点补充一个更简单的答案
通过在列表中添加行的数据,然后将该列表添加到字典中,您可以使用pd.DataFrame.from_dict(dict)
创建数据帧,而无需迭代
如果字典的每个值都是一行。您可以只使用:
pd.DataFrame.from_dict(dictionary,orient='index')
小例子:
我从SQL server返回了700K行数据。 所有这些都花了我太长时间。 以下方法大大缩短了时间
from collections import defaultdict
dict1 = defaultdict(list)
for row in results:
dict1['column_name1'] = row['column_name1']
dict1['column_name20'] = row['column_name20']
df = pd.DataFrame(dict1)
这就是我所需要的。如果你只需要每隔30秒添加一行,那么它真的需要高效吗?有什么理由它需要是一个数据帧吗?为什么不把它写进一个文件,然后在最后转换呢?@Stephen Rauch嗯,我希望我的样本尽可能每30秒保存一次。可能是错误的,我正在提取数据,然后将其添加到数据帧,然后使用
time.sleep(30)
直到获取下一组数据。我担心的是,加载时间将开始延长每个样本之间的时间。从这个问题上看,6000码似乎需要2.29秒。如果可能的话,我希望将这个数字保持在最小值。如果您担心30秒的睡眠时间会不准确,因为附加数据需要更长的时间,那么请修复睡眠时间next\u time+=30,time.sleep(next\u time-time.time())
@Stephen Rauch噢,这是个好主意!这是个好主意!可能有点超出我的能力
df.at[myindex,'A']=val1
df.at[myindex,'B']=val2
df.at[myindex,'C']=val3
%%timeit
test = pd.DataFrame({"A": [1,2,3], "B": [1,2,3], "C": [1,2,3]})
for i in range(0,1000):
testrow = pd.DataFrame([0,0,0])
pd.concat([test[:1], testrow, test[1:]])
%%timeit
test = pd.DataFrame({"A": [1,2,3], "B": [1,2,3], "C": [1,2,3]})
for i in range(0,1000):
test2 = pd.DataFrame({'A': 0, 'B': 0, 'C': 0}, index=[i+0.5])
test.append(test2, ignore_index=False)
test.sort_index().reset_index(drop=True)
%%timeit
test = pd.DataFrame({"A": [1,2,3], "B": [1,2,3], "C": [1,2,3]})
for i in range(0,1000):
test3 = [0,0,0]
test.loc[i+0.5] = test3
test.reset_index(drop=True)
# Dictionary containing the data
dic = {'row_1':['some','test','values',78,90],'row_2':['some','test','values',100,589]}
# Creation of the dataframe
df = pd.DataFrame.from_dict(dic,orient='index')
df
0 1 2 3 4
row_1 some test values 78 90
row_2 some test values 100 589
from collections import defaultdict
dict1 = defaultdict(list)
for row in results:
dict1['column_name1'] = row['column_name1']
dict1['column_name20'] = row['column_name20']
df = pd.DataFrame(dict1)