pandas 计数第二个 Dataframe 行值之外的多个 Dataframe 列的条目

b5buobof  于 2023-01-19  发布在  其他
关注(0)|答案(3)|浏览(142)

我有两个 Dataframe dfSpecs和dfData。dfSpecs包含dfData中参数列的规格限制。(LSL =规格下限,USL =规格上限)dfSpecs中的参数行不必与dfData中的参数列顺序相同。

dfSpecs = pd.DataFrame(
  {'param': ['parameter1', 'parameter2', 'parameter3'], 
  'LSL': [0, 10, 200], 
  'USL': [1, 20, 300]}
)
print(dfSpecs)
Output:
   param       LSL  USL
0  parameter1    0    1
1  parameter2   10   20
2  parameter3  200  300
dfData = pd.DataFrame(
  {'id': ['foo', 'foo', 'bar', 'bar'], 
  'x': [1, 1, 1, 1], 
  'y': [1, 2, 1, 2],
  'parameter1': [0.5, 0.6, 1.8, 0.4],
  'parameter2': [12, 14, 21, 15],
  'parameter3': [199, 301, 256, 234]}
)
print(dfData)
Output:
    id  x  y  parameter1  parameter2  parameter3
0  foo  1  1         0.5          12         199
1  foo  1  2         0.6          14         301
2  bar  1  1         1.8          21         256
3  bar  1  2         0.4          15         234

现在,我想获得一个新的 Dataframe ,其中包含超出参数和id规范限制的行数。
预期输出如下所示:

id       param  fail_count
0  foo  parameter1           0
1  foo  parameter2           0
2  foo  parameter3           2
3  bar  parameter1           1
4  bar  parameter2           1
5  bar  parameter3           0

以下代码可以工作,但速度非常慢(最多几分钟,取决于 Dataframe 大小):

fails=[]
for id, df in dfData.groupby('id'):
    for i, spec in dfSpecs.iterrows():
        fail_count = len(df[spec['param']].loc[(df[spec['param']]<spec['LSL']) | (df[spec['param']]>spec['USL'])])
        fails.append(id, [spec['param'], fail_count])
dfFails = pd.DataFrame(fails, columns=['id','parameter','fail_count'])
    • 有没有更简单、更快的方法来实现这一点?**我想在Pandas身上有更好的方法。

编辑:改写并添加示例输出

2izufjch

2izufjch1#

感谢您的回复,根据您的编辑,我建议如下:

# Create an empty dataframe with the columns 'id', 'param', and 'fail_count'
dfResults = pd.DataFrame(columns=['id', 'param', 'fail_count'])

# Get a list of unique ids in the 'dfData' dataframe
unique_ids = dfData['id'].unique()

# Iterate through the unique ids
for id in unique_ids:
    # Filter the 'dfData' dataframe for the current id
    df_id = dfData[dfData['id'] == id]
    
    # Get a list of unique parameters in the filtered dataframe
    unique_params = df_id.columns.difference(['id', 'x', 'y'])
    
    # Iterate through the unique parameters
    for param in unique_params:
        # Get the LSL and USL values for the current parameter
        LSL = dfSpecs[dfSpecs['param'] == param]['LSL'].values[0]
        USL = dfSpecs[dfSpecs['param'] == param]['USL'].values[0]
        
        # Calculate the number of failures for the current parameter and id
        fail_count = df_id[(df_id[param] < LSL) | (df_id[param] > USL)].shape[0]
        
        # Append the current id, parameter, and fail_count to the 'dfResults' dataframe
        dfResults = dfResults.append({'id': id, 'param': param, 'fail_count': fail_count}, ignore_index=True)
cygmwpex

cygmwpex2#

首先,您可以对dfData Dataframe 执行melt操作,然后使用dfSpecs查找transform中的left、right,以便与pd.Series.between一起使用,并将其作为新列fail_countassign一起分配,然后将groupby.sum()idparam一起分配,以获得计数。

specs = dfSpecs.set_index("param")
t = dfData.melt(
    value_vars=["parameter1", "parameter2", "parameter3"],
    id_vars=["id"],
    var_name="param"
)
out = t.assign(
    fail_count=t.groupby(["param"])["value"].transform(
        lambda x: ~x.between(specs.loc[x.name, "LSL"], specs.loc[x.name, "USL"])
    )
).groupby(["id", "param"])["fail_count"].sum().reset_index()

print(out)

    id       param  fail_count
0  bar  parameter1           1
1  bar  parameter2           1
2  bar  parameter3           0
3  foo  parameter1           0
4  foo  parameter2           0
5  foo  parameter3           2

或者你可以这样做(我觉得它比上面更有效):

specs = dfSpecs.set_index('param')
param_cols = dfData.filter(regex="parameter").columns.tolist()
t = (
    dfData[param_cols]
    .apply(lambda x: ~x.between(specs.loc[x.name, "LSL"], specs.loc[x.name, "USL"]))
    .assign(id=dfData["id"])
)
out = (
    t.melt(
        value_vars=param_cols, id_vars=["id"], var_name="param", value_name="fail_count"
    )
    .groupby(["id", "param"])
    .sum().reset_index()
)

或者,如果你认为融化很慢,你可以尝试堆叠,虽然我怀疑有多大区别。

out = (
    t.set_index("id")
    .rename_axis("param", axis=1)
    .stack()
    .groupby(level=[0, 1])
    .sum()
    .reset_index()
    .rename(columns={0: "fail_count"})
)
iqxoj9l9

iqxoj9l93#

最后,我使用panda的gtlt函数提出了自己的工作解决方案,首先循环遍历按id列分组的dfData Dataframe ,然后选择感兴趣的列(这里手动地,但是可以用真实的数据集上的.loc[]来完成)来自dfData)并对一系列dfSpecs['USL']dfSpecs['LSL']使用gtlt函数,其中'param'位于索引中,sum位于结果中。这给出了没有id列的pd.Series,因此我需要手动将它们和ffill()连接起来。第一行包含idNaN,因此我删除了该行。由于参数仍在索引中,因此我重置了索引以将param返回到 Dataframe 中。当然,我不得不对每个循环的结果进行concat,最后我重命名了列,因为它们在过程之后没有合理的名称。

dfResult = pd.DataFrame()
for id, df in dfData.groupby(['id']):
    fails = df[['parameter1', 'parameter2', 'parameter3']].gt(dfSpecs.set_index('param')['USL']).sum()+
            df[['parameter1', 'parameter2', 'parameter3']].lt(dfSpecs.set_index('param')['LSL']).sum()
    fails = pd.concat([pd.DataFrame({'id':[id]}),fails],axis=1,ignore_index=True).ffill().reset_index().drop(0)
    dfResult = pd.concat([dfResult,fails])
dfResult = dfResult.rename(columns={'index':'param',0:'id',1:'fail_count'}).reset_index(drop=True)

print(dfResult)

|    | param      | id   |   fail_count |
|---:|:-----------|:-----|-------------:|
|  0 | parameter1 | bar  |            1 |
|  1 | parameter2 | bar  |            1 |
|  2 | parameter3 | bar  |            0 |
|  3 | parameter1 | foo  |            0 |
|  4 | parameter2 | foo  |            0 |
|  5 | parameter3 | foo  |            2 |

问题中的代码在我的数据集上使用了25.6s。此代码仅使用了5.8s

相关问题