pandas 向 Dataframe 添加行的有效方法

nvbavucw  于 2022-12-16  发布在  其他
关注(0)|答案(8)|浏览(175)

从这个question和其他的看,似乎不推荐使用concatappend来构建panda Dataframe ,因为它每次都在重新复制整个 Dataframe 。
我的项目涉及到每30秒检索少量数据。这可能会运行3天的周末,所以有人可以很容易地期望超过8000行被创建一次一行。什么是最有效的方法来添加行到这个 Dataframe ?

lg40wkob

lg40wkob1#

我使用了这个答案的df.loc[i] = [new_data]建议,但是我有〉500,000行,这非常慢。
虽然给出的答案对OP的问题很好,但我发现在处理大量行时(而不是OP所描述的欺骗),使用csvwriter将数据添加到内存中的CSV对象,然后最后使用pandas.read_csv(csv)生成所需的DataFrame输出会更有效。

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

对于大约500,000行,速度提高了1000倍,并且随着行数的增加,速度提高只会更大(相比之下,the df.loc[1] = [data]将慢得多)
希望这能帮助那些在处理比OP更多的行时需要效率的人。

jogvjijk

jogvjijk2#

由于选择的答案完全错误,请在此处编辑。下面是对为什么不应使用放大设置的解释。“放大设置”实际上比追加更糟糕。
这里的tl;dr是**没有有效的方法可以使用DataFrame来实现这一点,所以如果您需要速度,应该使用另一种数据结构。**请参阅其他答案以获得更好的解决方案。

更多关于放大设置的信息

您可以使用loc在不存在的索引上向DataFrame添加行,但这也会执行所有数据的复制(请参见this discussion)。

In [119]: dfi
Out[119]: 
   A  B  C
0  0  1  0
1  2  3  2
2  4  5  4

In [120]: dfi.loc[3] = 5

In [121]: dfi
Out[121]: 
   A  B  C
0  0  1  0
1  2  3  2
2  4  5  4
3  5  5  5

对于所描述的用例,**放大设置实际上比append**多花费50%的时间:
使用append()时,8000行耗时6.59秒(每行0.8毫秒)

%%timeit df = pd.DataFrame(columns=["A", "B", "C"]); new_row = pd.Series({"A": 4, "B": 4, "C": 4})
for i in range(8000):
    df = df.append(new_row, ignore_index=True)

# 6.59 s ± 53.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

使用.loc()时,8000行需要10秒(每行1.25毫秒)

%%timeit df = pd.DataFrame(columns=["A", "B", "C"]); new_row = pd.Series({"A": 4, "B": 4, "C": 4})
for i in range(8000):
    df.loc[i] = new_row

# 10.2 s ± 148 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

更长的DataFrame怎么样?

与面向数据代码中的所有分析一样,YMMV和您应该针对您的用例对此进行测试。append和“放大设置”的写入时复制行为的一个特征是,使用较大的DataFrame会变得越来越慢:

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

使用此方法构建16 k行DataFrame所需的时间是8 k行的2.3倍。

ppcbkaq5

ppcbkaq53#

通过将一行的数据添加到列表中,然后将该列表添加到字典中,您就可以使用pd.DataFrame.from_dict(dict)创建一个 Dataframe ,而无需迭代。
如果字典的每个值都是一行,则可以只使用:pd.DataFrame.from_dict(dictionary,orient='index')
小例子:

# 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
azpvetkf

azpvetkf4#

您需要将问题分为两部分:
1.每30秒有效地接受数据(收集数据)。
1.一旦收集到数据就进行处理。
如果您的数据是关键的(也就是说,您不能丢失它)-将它发送到队列,然后从队列中批量读取它。
队列将提供可靠的(有保证的)接受,并且您的数据不会丢失。
您可以从队列中读取数据并将其转储到数据库中。
现在,Python应用程序只需要从数据库中读取数据,并以任何对应用程序有意义的时间间隔进行分析--也许您想要进行每小时的平均值;在这种情况下,您可以每小时运行一次脚本,从数据库中提取数据,并可能将结果写入另一个数据库/表/文件中。
底线是-将应用程序的收集和分析部分分开。

r6l8ljro

r6l8ljro5#

假设您的 Dataframe 已按顺序编制索引,您可以:
首先检查下一个索引值是什么以创建新行:

myindex = df.shape[0]+1

然后使用“at”写入每个所需的列

df.at[myindex,'A']=val1
df.at[myindex,'B']=val2
df.at[myindex,'C']=val3
eqzww0vc

eqzww0vc6#

我从SQL服务器返回了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)

这就是我想要的。

nc1teljy

nc1teljy7#

  • 圣丹斯 * 的答案在用法上可能是正确的,但是基准测试是错误的。正如 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个循环)

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

972 ms ± 14.4 ms/循环(7次运行的平均值±标准差,每次运行1个循环)

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

1.13 s ± 46 ms/循环(7次运行的平均值±标准差,每次运行1个循环)
当然,这纯粹是综合性的,我承认我没有预料到这些结果,但是看起来在索引不存在的情况下.loc.append的表现非常相似。

zdwk9cvp

zdwk9cvp8#

我的同事告诉我先做一个字典条目列表,然后把完成的列表推到 Dataframe 中,与一次推一个字典到 Dataframe 相比,列表方法是即时的。
这段代码筛选了大约54k条记录,只查找targ_datetime值之后的记录,然后将所需的值写回列表,再写回df_out:

df_out = pd.DataFrame()
df_len = df.count()
counter = 1
list_out = []
targ_datetime = datetime.datetime.fromisoformat('2021-12-30 00:00:00')
for rec in df.selectExpr("CAST(data as STRING) as data").take(df_len):
  j = jsonx.loads(rec[0])
  NewImage = j['dynamodb']['NewImage']
  NewImage['eventName'] = j['eventName']
  if j.get('dynamodb').get('NewImage').get('UPDATED_AT') != None:
    ts = datetime.datetime.fromisoformat(str(j['dynamodb']['NewImage']['UPDATED_AT']).replace('T', ' ')[0:-5])
  else:
    ts = datetime.datetime.fromtimestamp(j['dynamodb']['ApproximateCreationDateTime']/1000)
  if ts >= targ_datetime:
    #df_out = df_out.append(pd.Series(NewImage.values(), index=NewImage.keys()), ignore_index=True)
    j['dynamodb']['NewImage']['UPDATED_AT'] = ts
    list_out.append(NewImage)
    counter = counter +1
  #if counter > 10: break
df_out = pd.DataFrame(list_out)

相关问题