【问题标题】:Python: Working with dataframes of different sizes to create new columns based on datetime conditionsPython:使用不同大小的数据框根据日期时间条件创建新列
【发布时间】:2020-03-29 09:36:55
【问题描述】:

我在 Python 中有 2 个不同大小的数据框。较小的数据框有 2 个日期时间列,一个用于开始日期时间,一个用于结束日期时间。另一个数据框更大(更多的行和列)并且它有一个日期时间列。

df A

Date_hour_beginning   Date_hour_end
3/8/2019 18:35        3/8/2019 19:45
4/8/2019 14:22        4/8/2019 14:55

df 乙

Date_hour         compression
3/8/2019 18:37      41
3/8/2019 18:55      47
3/8/2019 19:30      55
3/8/2019 19:51      51
4/8/2019 14:10      53
4/8/2019 14:35      48
4/8/2019 14:51      51
4/8/2019 15:02      58

我想将压缩的平均值和幅度添加到涵盖日期时间范围的 df_A。得到以下结果:

df_A
Date_hour_beginning   Date_hour_end     mean_compression     amplitude
3/8/2019 18:35        3/8/2019 19:45        47.66              14
4/8/2019 14:22        4/8/2019 14:55        49.5               3

我尝试了 np.where 和 groupby 但我不知道但我遇到了数据框形状不匹配的错误。

【问题讨论】:

  • amplitude 中的值从何而来?
  • 最高减去最低值。第一行为 55-41,第二行为 51-48。
  • 您能提供创建这些数据帧的文件或代码吗?这样我们就不必重新编码所有内容。

标签: python pandas dataframe datetime


【解决方案1】:

这是我的解决方案。它是 eva-vw 的一种更冗长(也许更易读?)的版本。 eva-vw 使用 .apply() 方法,这是循环数据帧行的最快方法。但是,如果您的 df_A 确实有很多(很多)行(这里似乎不是这种情况),它只会在运行时间上产生显着差异。

for i, row in df_A.iterrows() :

    start = row['Date_hour_beginning']
    end = row['Date_hour_end']

    mask = (df_B['Date_hour'] >= start) & (df_B['Date_hour'] <= end) 
    compression_values = df_B.loc[mask, 'compression']

    df_A.loc[i, 'avg comp'] = compression_values.mean()
    df_A.loc[i, 'amp comp'] = compression_values.max() - compression_values.min()

为了完整起见,以下是我创建数据框的方式:

import numpy as np
import pandas as pd

columns = ['Date_hour_beginning', 'Date_hour_end']

times_1 = pd.to_datetime(['3/8/2019 18:35', '3/8/2019 19:45'])
times_2 =  pd.to_datetime(['4/8/2019 14:22', '4/8/2019 14:55'])

df_A = pd.DataFrame(data=[times_1, times_2], columns=columns)

data_B = [ ['3/8/2019 18:37',      41],
            ['3/8/2019 18:55',      47],
            ['3/8/2019 19:30',      55],
            ['3/8/2019 19:51',      51],
            ['4/8/2019 14:10',      53],
            ['4/8/2019 14:35',      48],
            ['4/8/2019 14:51',      51],
            ['4/8/2019 15:02',      58]]

columns_B = ['Date_hour', 'compression']
df_B = pd.DataFrame(data=data_B, columns=columns_B)

df_B['Date_hour'] = pd.to_datetime(df_B['Date_hour'])

更进一步:要解决您的问题,您需要遍历df_A 的行。这可以通过三种主要方式完成:(i) 在数据帧行的索引上使用普通的for 循环,(ii) 使用for 循环使用.iterrows() 方法,或使用apply()方法。

我在运行时将它们从最慢到最快排序。我选择了方法 (ii) 和 eva-vw 选择了方法 (iii)。 .apply() 的优点是它是最快的,但它的缺点(对我而言)是你必须在一行 lambda 函数中编写你想要对行执行的所有操作。

【讨论】:

  • 非常感谢,主持人!您的代码有效:))但是为什么 Eva 的 .apply 给了我: KeyError: ('compression', 'occured at index 0') ?
  • 在 eva-vw 的回答下方查看我的评论,希望对您有所帮助。更易读的代码也更容易调试,这就是我选择我的解决方案的原因!对答案感到满意后,不要忘记将问题标记为已回答。
【解决方案2】:
# create test dataframes
df_A = pd.DataFrame(
    {
        "Date_hour_beginning": ["3/8/2019 18:35", "4/8/2019 14:22"],
        "Date_hour_end": ["3/8/2019 19:45", "4/8/2019 14:55"],
    }
)
df_B = pd.DataFrame(
    {
        "Date_hour": [
            "3/8/2019 18:37",
            "3/8/2019 18:55",
            "3/8/2019 19:30",
            "3/8/2019 19:51",
            "4/8/2019 14:10",
            "4/8/2019 14:35",
            "4/8/2019 14:51",
            "4/8/2019 15:02",
        ],
        "compression": [41, 47, 55, 51, 53, 48, 51, 58],
    }
)

# convert to datetime
df_A['Date_hour_beginning'] = pd.to_datetime(df_A['Date_hour_beginning'])
df_A['Date_hour_end'] = pd.to_datetime(df_A['Date_hour_end'])
df_B['Date_hour'] = pd.to_datetime(df_B['Date_hour'])

# accumulate compression values per range
df_A["compression"] = df_A.apply(
    lambda row: df_B.loc[
        (df_B["Date_hour"] >= row["Date_hour_beginning"])
        & (df_B["Date_hour"] <= row["Date_hour_end"]),
        "compression",
    ].values.tolist(),
    axis=1,
)

# calculate mean compression and amplitude
df_A['mean_compression'] = df_A['compression'].apply(lambda x: sum(x) / len(x))
df_A['amplitude'] = df_A['compression'].apply(lambda x: max(x) - min(x))

【讨论】:

  • 谢谢伊娃!在运行累积时,我得到了这个: KeyError: ('compression', 'occured at index 0')
  • 嗯,如果您在 df_B 中没有 compression 列,会发生这种情况,但您做对了吗?
  • 添加了创建数据框的代码,如果您的数据看起来不同,请 lmk
  • @presenter 逗号只是一个格式化工件,不会导致 OP 的 KeyError。
  • 确保您可以将 if len(x) != 0 else np.nan 添加到每个 lambda 表达式的末尾(需要 import numpy as np
【解决方案3】:

groupby 可以接受同样索引的系列,即

df['Date_hour'] = pd.to_datetime(df['Date_hour'])

df_a['begin'] = pd.to_datetime(df_a['begin'])
df_a['end'] = pd.to_datetime(df_a['end'])

selector = df.apply(lambda x: df_a.query(f'begin <= \'{x["Date_hour"]}\' <= end').index[0], axis=1)

for i_gr, gr in df.groupby(selector):
    print(i_gr, gr)

然后继续你的 .mean() 或 .median()

【讨论】:

    【解决方案4】:

    使用这个:

    df_A['Date_hour_beginning'] = pd.to_datetime(df_A['Date_hour_beginning'])
    df_A['Date_hour_end'] = pd.to_datetime(df_A['Date_hour_end'])
    df_B['Date_hour'] = pd.to_datetime(df_B['Date_hour'])
    
    df_A = df_A.assign(key=1)
    df_B = df_B.assign(key=1)
    df_merge = pd.merge(df_A, df_B, on='key').drop('key',axis=1)
    
    df_merge = df_merge.query('Date_hour >= Date_hour_beginning and Date_hour <= Date_hour_end')
    df_merge['amplitude'] = df_merge.groupby(['Date_hour_beginning','Date_hour_end'])['compression'].transform(lambda x: x.max()-x.min())
    df_merge = df_merge.groupby(['Date_hour_beginning','Date_hour_end']).mean()
    

    输出:

                                             compression  amplitude
    Date_hour_beginning Date_hour_end                              
    2019-03-08 18:35:00 2019-03-08 19:45:00    47.666667       14.0
    2019-04-08 14:22:00 2019-04-08 14:55:00    49.500000        3.0
    

    【讨论】:

    • 谢谢你,路易吉吉! pd.merge 不起作用。它给了我一个内存错误。
    • 它没有指定。最后只是一些五颜六色的代码和内存错误:/
    猜你喜欢
    • 1970-01-01
    • 2021-11-04
    • 1970-01-01
    • 2021-08-06
    • 2020-04-06
    • 1970-01-01
    • 2022-11-12
    • 2020-06-25
    • 2022-01-14
    相关资源
    最近更新 更多