如何指定pandas数据帧将具有的行数?

时间:2015-04-13 15:23:45

标签: python pandas dataframe data-analysis real-time-data

我有一个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

3 个答案:

答案 0 :(得分: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

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

  • 数据的顺序。如果您需要相同顺序的数据,则需要使用datacounter进行切片以提取原始订单。
  • 行数很小。如果您最后添加的行数少于N,则您需要.dropna()(或计算插入的总行数)以删除未使用的行。

在我处理截断附加性能很重要的大多数场景中,上述两种情况都不正确,但您的场景可能不同。在这种情况下,@ Alexander有一个很好的解决方案,涉及.shift()

答案 1 :(得分:1)

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,并使用最大长度的模数来选择要覆盖的位置,同时将项目编号保留在DataFrame中。然后您可以按项目编号排序。在你的情况下,你可以想象按时间排序,但这种方法更为通用。与前面的示例一样,我将使用for循环来演示,但您可能没有。此外,我还假设您没有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)

答案 2 :(得分:1)

此示例初始化一个等于最大大小的DataFrame,并用Nones填充它。然后迭代一个新行列表,首先移动原始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