如何用Python Pandas计算时点值?

0 投票
2 回答
605 浏览
提问于 2025-04-28 19:54

我们有一个表格(pandas.DataFrame对象),里面显示了一个集群的CPU数量和内存使用情况。表格的每一行记录了某个工作的开始和结束时间(pandas.datetime对象),以及在这个时间段内的CPU和内存使用情况(因为工作是独立的,所以时间段可能会重叠):

In [505]: data.head()
Out[505]:
           created_at          deleted_at  vcpus  memory_mb
0 2013-11-08 18:26:34 2013-11-08 18:27:52      1        512
1 2013-11-08 18:27:53 2013-11-08 18:31:07      1        512
2 2013-11-08 18:30:24 2013-11-08 18:46:35      1       2000
3 2013-11-08 18:30:24 2013-11-08 18:46:31      1       2000
4 2013-11-08 18:30:25 2013-11-08 18:46:36      1       2000

我们想要创建一个图表,显示去年整年的总CPU和内存使用情况。

这就意味着我们需要知道,在某个时间点t,所有记录中created_at <= t <= deleted_atvcpus列的总和是多少。

我们可以用什么代码来完成这个任务呢?最快的解决方案是什么?(因为我们需要处理几百万条记录,所以速度非常重要。)

暂无标签

2 个回答

0

一种解决方案是使用Python的列表推导式,结合NumPy风格的整体数组索引:

# load data
data = pandas.read_csv('accounting.csv', sep='\t', parse_dates=['created_at', 'deleted_at'], na_values=['NULL'])

# create array of sampling times, using 1-minute resolution
mindate = data.created_at.min()
maxdate = data.deleted_at.max()
tm = pandas.date_range(mindate, maxdate, freq='Min')

# for each point in time `t`, select all records such that
# `created_at <= t <= deleted_at` and then take the sum of CPUs.
num_vcpus = pandas.Series(data['vcpus'][(data['created_at'] >= t) & (data['deleted_at'] <= t)].sum() for t in tm)

这样做是可行的,但有个缺点就是如果我们想计算,比如说内存使用情况,就得再次运行同样的生成器表达式。此外,我们还会创建很多临时的NumPy布尔数组来保存日期/时间比较的结果。

0

另一种解决方案是使用纯Python的循环:

  • 首先,创建一个采样时间的数组,
  • 接着,为我们想要计算的值创建NumPy数组:
    • 每一组独立值用一个数组(例如,CPU数量、内存)
    • 每个采样时间对应一个数组的条目
  • 对于每一行的DataFrame,在开始和结束时间之间的整个数组条目中添加相应的值

最后一点,如果时间是以某个起点(比如UNIX的“纪元”)为基准的秒数来表示,那么这个过程可以简化为比较简单的算术运算。

代码示例:

# load data file
data = pandas.read_csv('accounting.csv', sep='\t', parse_dates=['created_at', 'deleted_at'], na_values=['NULL'])

mindate = data.created_at.min()
maxdate = data.deleted_at.max()

# fill missing dates
data['deleted_at'] = pandas.to_datetime(data['deleted_at'].fillna(maxdate))
data['created_at'] = pandas.to_datetime(data['created_at'].fillna(mindate))


# inefficient conversion function
def epoch(date):
    return int(date.strftime("%s"))

# sample times, equally spaced at 1-minute interval
tm = pandas.date_range(mindate, maxdate, freq='Min')
ts=pandas.DataFrame(tm)
# convert datetime to UNIX epoch
epochs=ts[0].astype(pandas.np.int64)//10**9

minepoch=min(epochs)
step=60

vcpus = pandas.np.zeros(len(tm))
mem = pandas.np.zeros(len(tm))
for idx, row in data.iterrows():
    t0 = epoch(row[0])
    t1 = epoch(row[1])
    ix0 = (t0 - minepoch) / step
    ix1 = (t1 - minepoch) / step
    for index in range(ix0, ix1):
        vcpus[index] += row[5] # vcpus @ index 5
        mem[index] += row[6]   # memory_mb @ index 6

这样可以同时计算很多列,但循环还是在Python代码中,并且Pandas的.iterrows()函数需要为每一行创建一个pandas.Series对象——所以当行数很多时,这样做可能会效率不高。

撰写回答