【问题标题】:Splitting values from one data frame to another data frame based on certain conditions in another data frame in pandas根据熊猫中另一个数据帧中的某些条件将值从一个数据帧拆分到另一个数据帧
【发布时间】:2022-01-24 23:54:21
【问题描述】:

我有两个数据框 df1 和 df2,我想根据 df1 中的条件将值从 df2 调整为 df1。条件基于4个不同的列和不同ID的不同条件 在 df1 中,我需要将 df2 中的一列的值以这样一种方式放置,即它从 df2 中拆分值并在 df1 中调整它,并且每个 IDs 值的总和应该在两个数据帧中匹配。

所以我有以下格式的数据:

我想将 df2 中的值带到 df1 并根据 df1 本身中的 Start Day End Day、Start Time 和 End Time 拆分它,并且每个 ID 的 df1 和 df2 中的总和应该相等。

预期输出

这是在 pandas 中创建的相同数据框。这两个表是输入值,我想要上面的预期结果。

df1 = pd.DataFrame({'ID': ["Ch1","Ch1","Ch1","Ch1","Ch1","Ch1","Ch2","Ch2","Ch2"],
               'Start Day': [1,1,1,6,6,6,1,1,1], 
               'End Day': [5,5,5,7,7,7,7,7,7], 
               'Start Time': [600,1200,1700,600,1200,1700,700,1200,1700], 
               'End Time': [1200,1700,2500,1200,1700,2500,1200,1700,2400]})
print(df1)

df2 = pd.DataFrame({'ID': ["Ch1","Ch1","Ch1","Ch2","Ch2","Ch2","Ch2","Ch2","Ch2","Ch2","Ch2","Ch2","Ch2","Ch2"],
                    'Start Day': [1,1,1,1,1,1,1,1,1,1,1,1,6,1],
                    'End Day': [7,7,7,5,5,5,5,5,5,5,5,5,7,7],
                    'Start Time': [600,1200,1700,800,900,1000,1100,1200,1300,1900,2000,2200,700,700],
                    'End Time': [1200,1700,2500,900,1000,1100,1200,1300,1400,2000,2200,2300,2400,2400],
                    'Values':[1125,2250,1125,346.5,346.5,346.5,346.5,346.5,346.5,189,189,346.5,1795.5,346.5]})
print(df2)

谁能帮帮我。

计算:

从 df2 到 df1 说在 df2 我在第 1 天到第 7 天有值 1125,在 ch1 有 600 到 1200 的时间段我想在 df1 中将该值拆分为第 1 天到第 5 天和第 6 天到第 7 天,所以放值 1125 在 5 到 7 天,在同一时间段内,我将值除以 7 并将其乘以 5,并且由于时间段相同,即两个数据帧中的 600 到 1200,我将不再进一步划分并保留该值(1125/7*5) 或 (1125/df2(End Day- Start Day+1)*df1(End Day- Start Day+1)) 转换为 df1,其中 Start Day 和 End day 分别为 1 到 5,时间段为600 到 1200,该值将是 1125 中的 803.6,

同样,对于 df1 中的第 6 天到第 7 天,我们将以相同的方式拆分 df2 的值,从 df2 值列中我们将进行以下计算: (1125/7*2) 或 (1125/df2(End Day- Start Day+1)*df1(End Day- Start Day+1)) 转换为 df1,其中 Start Day 和 End day 分别为 6 到 7,时间段为600 到 1200,该值将是 1125 中的 321.4。

如果 df1 中的 Timeband 发生变化,我们将在 Values 中添加计算, 在 df1 中说,我希望开始日和结束日为 1 到 5,时间段为 700 到 1100,然后我将按以下方式将 df2 到 df1 的值:

(1125/7*5)*6/4 or (1125/df2(End Day-Start Day+1)*df1(End Day-Start Day+1))*df2(End Time-Start Time)/ df1(结束时间-开始时间)

另外,如果从 df2 开始,我们有开始日结束日 1 到 7,开始时间和结束时间 600 到 1200 为 1125,而在 df1 中,我们有开始日和结束日期 1 到 5,开始时间只有 700 到 1100,没有任何行中的其他日期带或时间带,然后在这种情况下将整个 1125 值保留到 df1 本身的该行中。

请帮我处理这段代码和逻辑,我会非常满意。 提前致谢。

【问题讨论】:

  • 你能分享一下预期输出的例子吗?
  • 嗨 @TomerS 分享了预期的输出
  • 我不明白预期输出中值的计算

标签: python pandas dataframe split logic


【解决方案1】:

在进一步澄清目标后更新答案:

这些结果与您为要查找的内容提供的示例相匹配,因此我相信我们现在处于同一页面上。
如果将其应用于庞大的数据集,这可能会有点慢,因为它调用 DataFrame.apply() 函数两次,遍历 df1 的每一行,对于 df1 的每一行,它遍历 df2 的每一行。

我试图捕捉需要以不同方式确定返回值的日期/时间块之间重叠的每种情况。您需要检查我没有错过任何其他场景/边缘情况。

解决办法如下:

def getDF2ValueForTimeBlock(df1row, df2row):
    if df2row["ID"] == df1row["ID"]:
        
        #Case 1: df2 window entirely contained within df1 window
        if (
            (df2row["Start Day"] >= df1row["Start Day"]) 
            & (df2row["End Day"] <= df1row["End Day"])
            & (df2row["Start Time"] >= df1row["Start Time"]) 
            & (df2row["End Time"] <= df1row["End Time"])
        ):    
            return df2row["Values"]
        
        #Case 2: df1 window entirely contained within df2 window
        elif (
            (df2row["Start Day"] <= df1row["Start Day"]) 
            & (df2row["End Day"] >= df1row["End Day"])
            & (df2row["Start Time"] <= df1row["Start Time"]) 
            & (df2row["End Time"] >= df1row["End Time"])
        ):    
            #Return only proportion of df2 values after scaling down to span of df1
            dayspanratio = (df1row["End Day"] - df1row["Start Day"] + 1) / (df2row["End Day"] - df2row["Start Day"] + 1)
            hourspanratio = (df1row["End Time"] - df1row["Start Time"]) / (df2row["End Time"] - df2row["Start Time"])
            return df2row["Values"] * dayspanratio * hourspanratio
        
        
        #Case 3: partial overlap on Days, df2 time completely within df1 time boundaries
        elif(
            (
                (df1row["Start Day"] <= df2row["Start Day"] <= df1row["End Day"])
                | (df1row["Start Day"] <= df2row["End Day"] <= df1row["End Day"])
            )
            &(
                (df1row["Start Time"] <= df2row["Start Time"] <= df2row["End Time"] <= df1row["End Time"])
            )
        ):
            #Find proportion of df2 values allocable to overlapping width of df1 window 
            maxStartDay = max([df1row["Start Day"], df2row["Start Day"]])
            minEndDay = min([df1row["End Day"], df2row["End Day"]])
            
            dayspanratio = (minEndDay - maxStartDay + 1) / (df2row["End Day"] - df2row["Start Day"] + 1)
            hourspanratio = 1
            return df2row["Values"] * dayspanratio * hourspanratio
        
        
        
        #Case 4: df2 window partially overlapping with df1 window on both Days and Time
        elif(
            (
                (df1row["Start Day"] <= df2row["Start Day"] <= df1row["End Day"])
                | (df1row["Start Day"] <= df2row["End Day"] <= df1row["End Day"])
            )
            &(
                (df1row["Start Time"] <= df2row["Start Time"] <= df1row["End Time"])
                | (df1row["Start Time"] <= df2row["End Time"] <= df1row["End Time"])
                #for df2 time extending beyond df1 time span on both boundaries:
                | (df2row["Start Time"] <= df1row["Start Time"] <= df1row["End Time"] <= df2row["End Time"]) 
            )
        ):
            #Find proportion of df2 values allocable to overlapping width of df1 window 
            maxStartDay = max([df1row["Start Day"], df2row["Start Day"]])
            minEndDay = min([df1row["End Day"], df2row["End Day"]])
            
            maxStartTime = max([df1row["Start Time"], df2row["Start Time"]])
            minEndTime = min([df1row["End Time"], df2row["End Time"]])
            
            dayspanratio = (minEndDay - maxStartDay + 1) / (df2row["End Day"] - df2row["Start Day"] + 1)
            hourspanratio = (minEndTime - maxStartTime) / (df2row["End Time"] - df2row["Start Time"])
            return df2row["Values"] * dayspanratio * hourspanratio
        
        
        #Case 5: Channel ID matches, but no overlap in both days and time windows
        else:
            return 0
        
    else:
        #Case Different Channel
        return 0


df1["Values"] = df1.apply(
    lambda d1row: df2.apply(lambda d2row: getDF2ValueForTimeBlock(d1row, d2row), axis=1).sum(), axis=1
)

print(df1)

输出:

    ID  Start Day  End Day  Start Time  End Time       Values
0  Ch1          1        5         600      1200   803.571429
1  Ch1          1        5        1200      1700  1607.142857
2  Ch1          1        5        1700      2500   803.571429
3  Ch1          6        7         600      1200   321.428571
4  Ch1          6        7        1200      1700   642.857143
5  Ch1          6        7        1700      2500   321.428571
6  Ch2          1        7         700      1200  2016.000000
7  Ch2          1        7        1200      1700  1323.000000
8  Ch2          1        7        1700      2400  1606.500000

【讨论】:

  • 嗨@BioData41我希望值在df2中的df1中,并且从df2到df1的值的拆分或合并应该以这样一种方式拆分或合并从df2到df1的值所以每个 ch1 的总和,即两个数据帧中的每个 ID 保持不变,因此如果 df2 中的 Ch1 值为 4500,则根据日期和时间段在 df1 中拆分的值应等于 4500,类似于 ch2
  • 嘿,@Anamtasayyed。我更新的答案是否符合您的需求?
  • 嗨@bioData41 它确实解决了我的问题,非常感谢您的支持。
猜你喜欢
  • 2020-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-02
  • 2022-01-20
  • 2019-08-20
  • 1970-01-01
相关资源
最近更新 更多