填充缺失数据并与主数据合并

2024-09-29 22:21:55 发布

您现在位置:Python中文网/ 问答频道 /正文

对于数据集DF,我想添加从辅助数据集TEMP获取的温度。 临时工并没有所有的日期。缺少的日期需要以这样一种方式进行插值,即为缺失的值填充最新的可用值。例如,如果2019-2-20缺少温度,但2019-2-19可用,则将填充此值。这可以通过使用pd.DataFrame(x.asfreq('D')).ffill().reset_index(drop=False)来完成。在

当每天有多个温度测量值时,应采用加权平均值。 在当前代码中,使用isin预先选择日期以限制数据大小。在

代码可以工作,但如果数据变大,则不是最佳的,特别是如果需要填写1000个地点和日期的温度数据并将其合并到DF中。我正在寻找一个关于时间/内存的更好的解决方案,例如基于itertools、apply、generator expression或其他任何东西。在

下面我展示了一个虚拟的小的可复制的代码示例。在

模块:

import numpy as np 
import pandas as pd

虚拟数据:

^{pr2}$

代码:

if 'temp' in DF.columns: 
    del DF['temp']
else: print('No variable temp found in DF')


def filltemps(dat1, dat2):
    """dat1: TEMP
    dat2: DF"""

    global totmp
    places = dat2['place'].unique()
    mx=len(places)
    totmp = pd.DataFrame(columns=['date', 'temp', 'place'])

    for i in range(mx):
        mp=[]
        dd1=[]
        nsp = pd.DataFrame(dat1[ (dat1['place']==places[i]) ])
        nsp = nsp[['date', 'quantity', 'temp']]
        prod = lambda w,z: w*z
        nsp['sumn'] = prod(nsp['temp'], nsp['quantity'])
        wavg = lambda y,x: y/x
        c3 = wavg(nsp.groupby('date')['sumn'].agg('sum'), nsp.groupby('date')['quantity'].agg('sum'))
        mp = pd.DataFrame(c3.asfreq('D')).ffill().reset_index(drop=False)
        mp.columns = ['date', 'temp']
        mp['place'] = np.array([places[i]] * len(mp))
        mp['date'] = pd.to_datetime(mp['date'])
        dd1 = dat2.loc[dat2['place']==places[i], ['date']]
        mp = mp[ mp['date'].isin(list(pd.to_datetime(dd1['date']))) ]
        totmp = pd.concat([totmp, mp])
    return totmp

%timeit TEMP2 = filltemps(TEMP, DF)计时显示每个环路116 ms±401µs(平均值±标准偏差,7次运行,每次10次循环)

在TEMP2中,可以看出,正如预期的那样,计算了C处的加权平均值:

TEMP2 = filltemps(TEMP, DF)

TEMP2将与原始DF合并。请注意,如果没有早期数据可用,则可能会缺少值。在

DF = pd.merge(DF, TEMP2, how='left', on=['date', 'place'])

DF,预期结果,应该是这样的:

enter image description here

非常感谢您的指点和帮助!在


Tags: 数据dataframedfdateplacemp温度temp
3条回答

这里是filltemps的替代品,它在我的机器上运行速度快10倍。它还使用apply,如请求:)

def interpolate_temps(temp_data, other_data):
    """ Interpolate temperature by place and location on a daily frequency.

    Parameters
         
    temp_data : pd.DataFrame
        with columns:
            date : datetime
            place : categorical
            temp : numerical
            quantity : numerical

    other_Data : pd.DataFrame
        with columns:
            date : datetime
            place : categorical

    Returns
       -
    pd.DataFrame
        With date and place in the index and a temp column
    """
    def weighted_average(df, col1, col2):
        """Average of col1 weighted by col2"""
        return (df[col1] * df[col2]).sum() / df[col2].sum()

    results = []
    for place in temp_data.place.unique():
        dates_needed = other_data[other_data.place == place].date.unique()
        result = (temp_data[temp_data.place==place]
                  .groupby('date')
                  .apply(weighted_average, 'temp', 'quantity')
                  .reindex(dates_needed, method='ffill')
                  .to_frame('temp')
                  .assign(place=place)
                  .set_index('place', append=True)
                 )
        results.append(result)
    return pd.concat(results)

它遵循与您相同的基本结构:

  1. 遍历每个地方。在

    1. 取按日期划分的温度加权平均值。

    2. ffill填充缺少的值。

    3. 添加“放置”作为列。

  2. 加入每个地方的结果

时间上的主要进步是在运行ffill之前将时间缩短到某个特定地点所需的日期。这也会提高内存效率,因为我们没有生成这么多命名的中间对象。代码中出现这一问题的最大地方是totmp = pd.concat([totmp, mp]),在这里您创建了len(place)不同版本的totmp,而在{}中,我们一直等到最后一次pd.concat(results)。在

interpolate_temps的结果在索引中有date和{},因此可以这样连接它:

^{pr2}$

或者像以前一样重置索引并合并。在

我唯一的建议是使用更详细的变量名。我从来没有真正弄明白其中的许多是什么意思,这使得理解代码变得更加困难。在

我的解决方案与ob Vaishali相似,但我想指出asfreq的一个陷阱。在

让我们从头开始。我们计算加权平均数:

TEMP.groupby(['date', 'place']).apply(lambda x: np.round(np.average(x['temp'],weights=x['quantity']), 2)).reset_index(name = 'temp')

现在我们计算完整的日期范围:

^{pr2}$

我们使用此数据范围重新编制温度索引:

TEMP = TEMP.groupby('place')['date', 'temp'].apply(lambda x: x.set_index('date').reindex(rng).ffill()).reset_index().rename(columns={'level_1': 'date'})

as_freq相反,我们现在还将处理温度时间序列比位置序列“短”的情况。在

最后,我们可以把所有的东西放在一起:

DF.merge(TEMP, on=['date', 'place'])

如果我们通过更改最后日期来稍微修改输入:

DF = pd.DataFrame({'date': ['2019-01-01', '2019-01-11', '2019-01-13', '2019-01-14', '2019-01-22', '2019-02-15'], 'place':['A', 'A','A','A','B','C']})

Ruthger的解决方案提供了:

        date place  temp
1 2019-01-11     A   5.0
3 2019-01-13     A   5.0
4 2019-01-14     A   7.0
0 2019-01-22     B  10.0

瓦西里:

        date place  temp
0 2019-01-01     A   NaN
1 2019-01-11     A   5.0
2 2019-01-13     A   5.0
3 2019-01-14     A   7.0
4 2019-01-22     B  10.0
5 2019-02-15     C   NaN

乔格:

0 2019-01-01     A    NaN
1 2019-01-11     A   5.00
2 2019-01-13     A   5.00
3 2019-01-14     A   7.00
4 2019-01-22     B  10.00
5 2019-02-15     C  10.75

简要介绍:

%timeit Ruthger(TEMP, DF)
526 ms ± 36.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit Vaishali(TEMP, DF)
12.1 ms ± 135 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit Joerg(TEMP, DF)
14.9 ms ± 872 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

计算加权平均温度。在TEMP中按位置填充缺少的日期,并将结果数据帧与DF合并。在

TEMP.date = pd.to_datetime(TEMP.date) 

TEMP = TEMP.groupby(['date', 'place']).apply(lambda x: np.round(np.average(x['temp'],weights=x['quantity']), 2)).reset_index(name = 'temp')

DF.merge(TEMP.set_index('date').groupby('place')['temp'].apply(lambda x: x.asfreq('D', method='ffill')).reset_index(), 'left')

    date        place   temp
0   2019-01-01  A       NaN
1   2019-01-11  A       5.00
2   2019-01-13  A       5.00
3   2019-01-14  A       7.00
4   2019-01-22  B       10.00
5   2019-02-14  C       10.75

相关问题 更多 >

    热门问题