如何在 for 循环中附加熊猫数据框中的行?

Posted

技术标签:

【中文标题】如何在 for 循环中附加熊猫数据框中的行?【英文标题】:How to append rows in a pandas dataframe in a for loop? 【发布时间】:2015-10-18 21:44:09 【问题描述】:

我有以下 for 循环:

for i in links:
     data = urllib2.urlopen(str(i)).read()
     data = json.loads(data)
     data = pd.DataFrame(data.items())
     data = data.transpose()
     data.columns = data.iloc[0]
     data = data.drop(data.index[[0]])

这样创建的每个数据框的大多数列都与其他列相同,但不是全部。而且,它们都只有一排。我需要将 for 循环生成的每个数据帧中的所有不同列和每一行添加到数据帧中

我尝试了 pandas concatenate 或类似方法,但似乎没有任何效果。任何想法?谢谢。

【问题讨论】:

由于您事先不知道列,这似乎是 Pandas.DataFrame 的设计目的,您可能应该生成一个巨大的列表列表,使用 np.unique() 等来生成完整的列列表;在循环结束时,从 List 中创建 DataFrame。 【参考方案1】:

假设您的数据如下所示:

import pandas as pd
import numpy as np

np.random.seed(2015)
df = pd.DataFrame([])
for i in range(5):
    data = dict(zip(np.random.choice(10, replace=False, size=5),
                    np.random.randint(10, size=5)))
    data = pd.DataFrame(data.items())
    data = data.transpose()
    data.columns = data.iloc[0]
    data = data.drop(data.index[[0]])
    df = df.append(data)
print('\n'.format(df))
# 0   0   1   2   3   4   5   6   7   8   9
# 1   6 NaN NaN   8   5 NaN NaN   7   0 NaN
# 1 NaN   9   6 NaN   2 NaN   1 NaN NaN   2
# 1 NaN   2   2   1   2 NaN   1 NaN NaN NaN
# 1   6 NaN   6 NaN   4   4   0 NaN NaN NaN
# 1 NaN   9 NaN   9 NaN   7   1   9 NaN NaN

那么就可以换成

np.random.seed(2015)
data = []
for i in range(5):
    data.append(dict(zip(np.random.choice(10, replace=False, size=5),
                         np.random.randint(10, size=5))))
df = pd.DataFrame(data)
print(df)

换句话说,不要为每一行形成一个新的DataFrame。相反,收集字典列表中的所有数据,然后在循环外最后调用一次df = pd.DataFrame(data)

df.append 的每次调用都需要为新的DataFrame 分配一个额外行的空间,将原始DataFrame 中的所有数据复制到新DataFrame 中,然后将数据复制到新行中。所有这些分配和复制使得在循环中调用df.append 非常低效。使用行数复制grows quadratically 的时间成本。 call-DataFrame-once 代码不仅更容易编写,它的性能也会更好——复制的时间成本随着行数线性增长。

【讨论】:

我对性能差异感到非常惊讶:使用 dataframe.append 添加 100 行 5 个值需要 336ms (我试过 dataframe.loc[i] ,它似乎是一样的)与提出的解决方案仅需 4.8 毫秒!【参考方案2】:

您可以在循环中追加行的原因有 2 个,1. 添加到现有的 df,2. 创建一个新的 df。

要创建一个新的 df,我认为它有据可查,您应该将数据创建为列表,然后创建数据框:

cols = ['c1', 'c2', 'c3']
lst = []
for a in range(2):
    lst.append([1, 2, 3])
df1 = pd.DataFrame(lst, columns=cols)
df1
Out[3]: 
   c1  c2  c3
0   1   2   3
1   1   2   3

或者,使用索引创建数据框,然后添加到它

cols = ['c1', 'c2', 'c3']
df2 = pd.DataFrame(columns=cols, index=range(2))
for a in range(2):
    df2.loc[a].c1 = 4
    df2.loc[a].c2 = 5
    df2.loc[a].c3 = 6
df2
Out[4]: 
  c1 c2 c3
0  4  5  6
1  4  5  6

如果要添加到现有数据框,可以使用上述任一方法,然后将 df 附加在一起(带或不带索引):

df3 = df2.append(df1, ignore_index=True)
df3
Out[6]: 
  c1 c2 c3
0  4  5  6
1  4  5  6
2  1  2  3
3  1  2  3

或者,您也可以创建一个字典条目列表并按照上面的答案附加这些条目。

lst_dict = []
for a in range(2):
    lst_dict.append('c1':2, 'c2':2, 'c3': 3)
df4 = df1.append(lst_dict)
df4
Out[7]: 
   c1  c2  c3
0   1   2   3
1   1   2   3
0   2   2   3
1   2   2   3

使用 dict(zip(cols, vals)))

lst_dict = []
for a in range(2):
    vals = [7, 8, 9]
    lst_dict.append(dict(zip(cols, vals)))
df5 = df1.append(lst_dict)

包括以下评论中的想法:

事实证明,Pandas 确实有一种有效的方法来附加到数据框:

df.loc( len(df) ) = [new, row, of, data] 

(this) 将就地“附加”到数据帧的末尾。 – 黛米斯 3 月 22 日 15:32

【讨论】:

谢谢,我没有发现它清楚地记录了不建议在循环期间动态创建 DF - 在循环期间就地创建最终数据结构而不是临时创建似乎是合乎逻辑的事情首先列出(这会导致您制作了两个相同数据的数组)。为什么不鼓励附加到数据框,而不是先生成列表?如果您有大量数据集,听起来这将使用两倍的资源。 已经有一段时间了,但如果我没记错的话,当你追加时,你最终会以某种方式复制整个内容。 ***.com/questions/55967976/… (我不是专门指“append()”方法。)Pandas 没有一种干净的方式来就地添加到数据集?喜欢 List.append 方法吗?令人惊讶的是,数据分析包需要复制完整的数据集才能创建它。 不好意思不是我写的,我就是用的。 事实证明,Pandas 确实有一种有效的方法来附加到数据帧:df.loc( len(df) ) = [new, row, of, data] 将“附加”到数据帧的末尾。【参考方案3】:

首先,创建一个带有列名的空 DataFrame,然后,在 for 循环中,您必须定义一个字典(一行),其中包含要附加的数据:

df = pd.DataFrame(columns=['A'])
for i in range(5):
    df = df.append('A': i, ignore_index=True)
df
   A
0  0
1  1
2  2
3  3
4  4

如果你想添加更多列的行,代码如下所示:

df = pd.DataFrame(columns=['A','B','C'])
for i in range(5):
    df = df.append('A': i,
                    'B': i * 2,
                    'C': i * 3,
                   
                   ,ignore_index=True
                  )
df
    A   B   C
0   0   0   0
1   1   2   3
2   2   4   6
3   3   6   9
4   4   8   12

Source

【讨论】:

【参考方案4】:

一种更紧凑、更有效的方法可能是:

cols = ['frame', 'count']
N = 4
dat = pd.DataFrame(columns = cols)
for i in range(N):

    dat = dat.append('frame': str(i), 'count':i,ignore_index=True)

输出将是:

>>> dat
   frame count
0     0     0
1     1     1
2     2     2
3     3     3

【讨论】:

为什么这样高效?在内存、时间或代码量上是否有效?看起来它会使用两倍的内存,因为每次迭代都必须用相同的 DF 覆盖整个 DF。【参考方案5】:

借助一个临时的空数据框,我在 for 循环中创建了一个数据框。因为对于 for 循环的每次迭代,都会创建一个新的数据框,从而覆盖之前迭代的内容。

因此我需要将数据框的内容移动到已经创建的空数据框。就这么简单。我们只需要使用 .append 函数,如下所示:

temp_df = pd.DataFrame() #Temporary empty dataframe
for sent in Sentences:
    New_df = pd.DataFrame('words': sent.words) #Creates a new dataframe and contains tokenized words of input sentences
    temp_df = temp_df.append(New_df, ignore_index=True) #Moving the contents of newly created dataframe to the temporary dataframe

在for循环之外,可以将临时数据框的内容复制到主数据框中,如果不需要,可以删除临时数据框

【讨论】:

以上是关于如何在 for 循环中附加熊猫数据框中的行?的主要内容,如果未能解决你的问题,请参考以下文章

确定熊猫数据框中的列值何时更改

如何获取熊猫数据框中的行,列中具有最大值并保留原始索引?

如何查找在熊猫数据框中出现最多的行项目

将列表作为大熊猫中的行附加数据框

如何在循环中附加多个熊猫数据框?

如何按 > 日期对一系列日期求和并将它们附加到熊猫新数据框中的新列?