如何通过考虑其他数据框的条件在python中生成新的数据框?

时间:2019-07-17 21:39:05

标签: python pandas numpy data-manipulation

我正在python中使用pandas在一个非常大的数据集(例如1亿行)上执行数据操作。我有两个数据框,并希望根据所提到的条件生成第三个数据框,以下说明了该情况:

数据框1:
Col_B和Col_D属于int64类型

Col_A   Col_B   Col_C   Col_D
 A       11      B       20
 A       11      C       24
 B       14      R       32
...      ...    ...      ...

数据框2:
Col_Z类型为float64,其余列为int64

Col_X   Col_Y   Col_P   Col_Q   Col_Z
 10      15      16      21      0.99
 10      15      17      22      0.89
...     ...     ...     ...      ...
...     ...     ...     ...      ...
 11      15      30      35      0.67
...     ...     ...     ...      ...

要应用的条件: 为了理解条件,只考虑两个数据框的第一行:

  

如果(Col_B的值在Col_X和Col_Y的值之间)和(Col_D的值在Col_P和Col_Q的值之间),则返回相应的Col_A,Col_C和Col_Z的值,否则返回NaN

预期输出(数据框3):

Col_A   Col_C   Col_Z
 A       B       0.99
NaN     NaN      NaN
 B       R       0.67

注意:生成此输出仅考虑数据框中是否只有这三行,但实际上,数据框1的每个值都必须扫描数据框2中的所有值,直到达到所需条件为止。

我的代码:

df3 = {}
Col_A = []
Col_C = []
Col_Z = []
for i in df1.iterrows():    
    value = float(df2[(i[1][1] > df2['Col_X'].values) &
      (i[1][1] < df2['Col_Y'].values) &
      (i[1][3] > df2['Col_P'].values) &
      (i[1][3] < df2['Col_Q'].values)]['Col_Z'])

    if bool(value):
        Col_Z.append(value)
        Col_A.append(i[1][0])
        Col_C.append(i[1][2])
    else:
        Col_Z.append(float('NaN'))
        Col_A.append(float('NaN'))
        Col_C.append(float('NaN'))

直到满足条件为止,此代码才能正常工作,一旦不满足条件,它将引发TypeError。请可以对此进行纠正。

另外,我想知道是否有其他有效的替代方法来执行它。请告诉我。

2 个答案:

答案 0 :(得分:2)

新答案

您有两个嵌入式循环。第一个总是完整循环。但不是第二个。因此,问题在于如何提高子局部循环的效率。

在这里,我为您提供两种执行第二个循环的方法:

  • 第一个将数据集视为一个整体,处理所有数据并选择有趣的值
  • 第二个迭代行直到匹配条件。

discussion可能会给您一些有关如何执行迭代的见识。

# Import module
import numpy as np

df1 = pd.DataFrame([["A", 11,  "B", 20],
                    ["A", 11,  "C", 24],
                    ["B", 14,  "R", 32]],
                   columns=["Col_A", "Col_B", "Col_C", "Col_D"])
df2 = pd.DataFrame([[10, 15,  16, 21, 0.99],
                    [10, 15,  17, 22, 0.89],
                    [11, 15,  30, 35, 0.67]],
                   columns=["Col_X", "Col_Y", "Col_P", "Col_Q", "Col_Z"])

def getCondition(row, df2):
    # Iterate df2 till a row meets the condition
    for _, row_df2 in df2.iterrows():
        if row_df2.Col_X <= row.Col_B and row.Col_B < row_df2.Col_Y \
            and row_df2.Col_P <= row.Col_D and row.Col_D < row_df2.Col_Q:
            return pd.Series([row.Col_A, row.Col_C, row_df2.Col_Z])
    return np.NaN


def getCondition2(row, df2):
    # Find all rows matching the condition and select the first
    condition = ((df2.Col_X <= row.Col_B) & (row.Col_B < df2.Col_Y)\
        & (df2.Col_P <= row.Col_D) & (row.Col_D < df2.Col_Q))
    if sum(condition) > 0:
        return pd.Series([row.Col_A, row.Col_C, df2.Col_Z[condition].iloc[0]])
    return np.NaN


# Apply the condition
output = df1.apply(getCondition2, args=[df2], axis=1)
print(output)
#      0    1     2
# 0    A    B  0.99
# 1  NaN  NaN   NaN
# 2    B    R  0.67

旧答案

您可以通过将数据集作为一个整体来考虑。

  • 首先,为方便起见,建议您加入两个数据集作为一个数据集。您可以使用merge函数或仅使用concat来实现。在这里,我使用concat,因为另一个解决方案使用了merge。要了解其中的表现,您可以查看this
  • 然后,您可以在整个列上定义条件。照顾成为and的{​​{1}}运算符。
  • 最后,您可以调用where函数,该函数在不满足条件时返回&

  • 要适合所需的输出,可以使用iloc或仅调用列名来过滤列。

代码在这里:

Nan

答案 1 :(得分:0)

经过几次试验,我能够解决自己的代码。这是-已纠正的一个:

df3 = {}
Col_A = []
Col_C = []
Col_Z = []
for i in df1.iterrows():    
    value = df2[(i[1][1] > df2['Col_X'].values) &
      (i[1][1] < df2['Col_Y'].values) &
      (i[1][3] > df2['Col_P'].values) &
      (i[1][3] < df2['Col_Q'].values)]['Col_Z']

    if value.empty:
        Col_Z.append(float('NaN'))
        Col_A.append(float('NaN'))
        Col_C.append(float('NaN'))
    else:
        Col_Z.append(value)
        Col_A.append(i[1][0])
        Col_C.append(i[1][2])

df3['A'] = Col_A
df3['C'] = Col_C
df3['Z'] = Col_Z
df3 = pd.DataFrame(df3)

但是,由于for循环遍历所有行,因此该方法可能不适用于大型数据集,例如100-200百万。 希望找到一些更有效的方法!