5

我有一个 Pandas 数据框,我不断地每秒添加一行数据,如下所示。

df.loc[time.strftime("%Y-%m-%d %H:%M:%S")] = [reading1, reading2, reading3]
>>>df
                     sensor1 sensor2 sensor3
2015-04-14 08:50:23    5.4     5.6     5.7
2015-04-14 08:50:24    5.5     5.6     5.8
2015-04-14 08:50:26    5.2     5.3     5.4

如果我继续这样做,最终我将开始遇到内存问题(每次它都会调用整个 DataFrame)。

我只需要保留 X 行数据。即操作后,它将是:

>>>df
                     sensor1 sensor2 sensor3
(this row is gone)
2015-04-14 08:50:24    5.5     5.6     5.8
2015-04-14 08:50:26    5.2     5.3     5.4
2015-04-14 08:50:27    5.2     5.4     5.6

有没有一种方法可以指定最大行数,以便在添加任何后续行时,同时删除最旧的行,而无需“检查 DataFrame 的长度,如果 DataFrame 的长度 > X,删除第一行,追加新行”?

像这样,但对于 Pandas DataFrame:https ://stackoverflow.com/a/10155753/4783578

4

3 回答 3

2

pandas将数据存储在数组中。执行您想要的那种操作本质上需要一个数组数据结构的副本。由于数据存储在连续(或跨步)内存中,因此在末尾添加某些内容并从开头删除某些内容需要将所有内容复制到新的内存区域。没有办法解决这个问题。您需要使用不同的数据结构。

编辑:再想一想,我看到了两种方法来做到这一点。

最简单和最直接的方法是使用一个collections.deque元组。您可以在末尾追加一个新元组,如果它太满,它将从头开始转储相应的元组。最后,您可以将它们转换为DataFrame. 我只是以for循环为例,我收集您以不同的方式获取数据。没关系:

import pandas as pd
from collections import deque

maxlen = 1000

dq = deque(maxlen=maxlen)

for reading1, reading3, reading3 in readings:
    dq.append(pd.Series([reading1, reading2, reading3], 
                        index=['sensor1', 'sensor2', 'sensor3'], 
                        name=time.strftime("%Y-%m-%d %H:%M:%S")))

df = pd.concat(dq, axis=1).T

第二种方法是使用DataFrame一个固定大小的a,并使用最大长度的模来选择要覆盖的地方,同时还要保留DataFrame. 然后您可以按项目编号排序。在您的情况下,您可以想象按时间排序,但这种方法更通用。和前面的例子一样,我将使用一个for循环来演示,但你可能没有。此外,我还将假设您没有真正的 iterable you can enumerate,如果您这样做了,那么您不必像我在这里所做的那样跟踪索引号:

import pandas as pd

maxlen = 1000

df = pd.DataFrame(np.full((maxlen, 5), np.nan),
                  columns=['index', 'time', 
                           'sensor1', 'sensor2', 'sensor3'])

i = 0
for reading1, reading3, reading3 in readings:
    df.loc[i%maxlen, :] = [i, time.strftime("%Y-%m-%d %H:%M:%S"),
                           reading1, reading2, reading3]
    i+=1

df.sort('index', inplace=True)
del df['index']
df.set_index('time', drop=True, inplace=True)
于 2015-04-13T15:28:29.213 回答
2

一种方法是预先分配行,并循环替换这些值。

# Say we to limit to a thousand rows
N = 1000

# Create the DataFrame with N rows and 5 columns -- all NaNs
data = pd.DataFrame(pd.np.empty((N, 5)) * pd.np.nan) 

# To check the length of the DataFrame, we'll need to .dropna().
len(data.dropna())              # Returns 0

# Keep a running counter of the next index to insert into
counter = 0

# Insertion always happens at that counter
data.loc[counter, :] = pd.np.random.rand(5)

# ... and increment the counter, but when it exceeds N, set it to 0
counter = (counter + 1) % N

# Now, the DataFrame contains one row
len(data.dropna())              # Returns 1

# We can add several rows one after another. Let's add twice as many as N
for row in pd.np.random.rand(2 * N, 5):
    data.loc[counter, :] = row
    counter = (counter + 1) % N

# Now that we added them, we still have only the last N rows
len(data)                       # Returns N

这避免了以任何方式修改数据的需要,并且将是附加数据的快速方法。但是,如果出现以下情况,从数据中读取可能会更慢:

  • 数据的顺序很重要。如果需要相同顺序的数据,则需要使用切片data提取counter原始顺序。
  • 行数很少。如果最终添加的行数少于N,则需要.dropna()(或计算插入的总行数)来删除未使用的行。

在我处理的大多数情况下,截断追加性能很重要,以上都不是真的,但你的情况可能会有所不同。在这种情况下,@Alexander 有一个很好的解决方案,涉及.shift().

于 2015-04-14T02:30:43.133 回答
1

此示例初始化一个等于最大大小的 DataFrame 并用 None 填充它。然后它遍历新行列表,首先移动原始 DataFrame,然后将新行附加到末尾。您没有指定要如何处理索引,所以我忽略了它。

max_rows = 5
cols = list('AB')

# Initialize empty DataFrame
df = pd.DataFrame({c: np.repeat([None], [max_rows]) for c in cols})

new_rows = [pd.DataFrame({'A': [1], 'B': [10]}), 
            pd.DataFrame({'A': [2], 'B': [11]}),
            pd.DataFrame({'A': [3], 'B': [12]}),
            pd.DataFrame({'A': [4], 'B': [13]}),
            pd.DataFrame({'A': [5], 'B': [14]}),
            pd.DataFrame({'A': [6], 'B': [15]}),
            pd.DataFrame({'A': [7], 'B': [16]})]

for row in new_rows:
    df = df.shift(-1)
    df.iloc[-1, :] = row.values

>>> df
df
   A   B
0  3  12
1  4  13
2  5  14
3  6  15
4  7  16

让我们用一个真实的例子来说明 AAPL 一年的股票价格。

from datetime import timedelta

aapl = DataReader("AAPL", data_source="yahoo", start="2014-1-1", end="2015-1-1")
cols = aapl.columns
df = pd.DataFrame({c: np.repeat([None], [max_rows]) for c in aapl.columns})[cols]
# Initialize a datetime index
df.index = pd.DatetimeIndex(end=aapl.index[0] + timedelta(days=-1), periods=max_rows, freq='D')

for timestamp, row in aapl.iterrows():
    df = df.shift(-1)
    df.iloc[-1, :] = row.values
    idx = df.index[:-1].tolist()
    idx.append(timestamp)
    df.index = idx

>>> df
              Open    High     Low   Close       Volume Adj Close
2013-12-28  112.58  112.71  112.01  112.01  1.44796e+07    111.57
2013-12-29   112.1  114.52  112.01  113.99   3.3721e+07    113.54
2013-12-30  113.79  114.77   113.7  113.91  2.75989e+07    113.46
2013-12-31  113.64  113.92  112.11  112.52  2.98815e+07    112.08
2014-12-31  112.82  113.13  110.21  110.38  4.14034e+07    109.95
于 2015-04-13T21:04:53.827 回答