如何在for循环中向pandas数据框中添加行?

107

我有以下的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连接或类似的方法,但似乎都不起作用。有什么建议吗?谢谢。

由于您事先不知道列,这似乎是Pandas.DataFrame的设计目的,因此您应该生成一个巨大的列表,使用np.unique()等来生成完整的列列表;在循环结束时,从列表中创建DataFrame。 - Demis
5个回答

113
假设您的数据长这样:
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非常低效。复制的时间成本随着行数的增加呈二次方增长。不仅调用一次DataFrame代码更容易编写,而且其性能也会更好——复制的时间成本随行数线性增长。

7
我对性能差异感到非常惊讶:使用dataframe.append添加100行、每行5个值需要336毫秒的时间(我试过dataframe.loc[i],看起来效果是一样的)。而所提出的解决方案只需要4.8毫秒! - 2diabolos.com
注意:.append在未来的pandas版本中将被弃用,请使用pd.concat代替。 - jonnyg23

106

在循环中添加行的原因有两个,1. 添加到现有的数据框中,2. 创建一个新的数据框。

要创建一个新的数据框,我认为应该将数据创建为列表,然后再创建数据框,这一点已经得到了很好的记录:

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

如果您想要添加到现有的数据框中,您可以使用上述任一方法,然后将数据框连接在一起(可以选择保留或不保留索引):

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] 

这将会在原地向数据框的末尾“追加”内容。- Demis Mar 22 at 15:32

谢谢,我没有清楚地找到文档说明在循环期间动态创建DF不被推荐 - 在循环期间创建最终数据结构似乎是一件合理的事情,而不是首先创建一个临时列表(这会导致您已经制作了两个相同数据的数组)。为什么不建议将数据附加到DataFrame中,而是首先生成列表?如果您有巨大的数据集,那么这似乎会使用两倍的资源。 - Demis
已经有一段时间了,但如果我没记错的话,当你进行追加操作时,最终会以某种方式复制整个内容。https://stackoverflow.com/questions/55967976/python-panda-append-dataframe-in-loop?noredirect=1&lq=1 - kztd
抱歉,我没有编写它,我只是使用它。 - kztd
原来Pandas确实有一种有效的方式来将数据附加到数据框中:df.loc( len(df) ) = [new, row, of, data] 将在原地“附加”到数据框末尾。 - Demis
听起来很棒。 - kztd
显示剩余2条评论

15

也许更紧凑高效的方法是:

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

2
这为什么是高效的?高效在内存、时间或代码量方面?看起来它将使用两倍的内存,因为每次迭代都必须用相同的DF覆盖整个DF。 - Demis

4

我使用了一个临时空数据框,在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 循环之外,您可以将临时数据帧的内容复制到主数据帧中,然后如果不需要它,则删除临时数据帧。


4

首先,创建一个有列名的空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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接