Python—向dataframe添加行的有效方法

Python—向dataframe添加行的有效方法,python,pandas,numpy,dataframe,Python,Pandas,Numpy,Dataframe,从这一点和其他方面来看,似乎不建议使用concat或append来构建熊猫数据帧,因为每次都会重新复制整个数据帧 我的项目涉及每30秒检索少量数据。这可能会运行3天的周末,因此有人很容易期望一次创建一行超过8000行。向该数据框添加行的最有效方法是什么?您需要将问题分为两部分: 每30秒有效地接受数据(收集数据) 收集数据后处理数据 如果您的数据是关键的(也就是说,您不能承担丢失数据的代价),请将其发送到队列,然后从队列中批量读取数据 队列将提供可靠的(保证的)接受,并且您的数据不会丢失 您可以

从这一点和其他方面来看,似乎不建议使用
concat
append
来构建熊猫数据帧,因为每次都会重新复制整个数据帧


我的项目涉及每30秒检索少量数据。这可能会运行3天的周末,因此有人很容易期望一次创建一行超过8000行。向该数据框添加行的最有效方法是什么?

您需要将问题分为两部分:

  • 每30秒有效地接受数据(收集数据)
  • 收集数据后处理数据
  • 如果您的数据是关键的(也就是说,您不能承担丢失数据的代价),请将其发送到队列,然后从队列中批量读取数据

    队列将提供可靠的(保证的)接受,并且您的数据不会丢失

    您可以从队列中读取数据并将其转储到数据库中

    现在,您的Python应用程序只需从数据库中读取数据,并以对应用程序有意义的任何时间间隔进行分析——也许您希望每小时进行一次平均;在这种情况下,您将每小时运行一次脚本,从数据库中提取数据,并可能将结果写入另一个数据库/表/文件中


    底线-拆分应用程序的收集和分析部分。

    在此处编辑所选答案,因为它完全错了。下面解释了为什么不应使用放大设置“放大设置”实际上比追加更糟糕。

    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)