如何根据Pandas中相邻列中的值计算跨列的平均值

z6psavjg  于 2023-11-15  发布在  其他
关注(0)|答案(5)|浏览(135)

我附上了一个示例数据集。我的实际数据集要大得多。“yr”列被分为“cd”和“qty”,从yr 10到yr 1。不是每行都包含完整的数据集,有些包含零。示例数据:
| 销售|YR10CD| 10年数量|YR9CD|公司简介|YR8CD| y8qty|
| --|--|--|--|--|--|--|
| 42 |一| 45 |一| 47 |一| 49 |
| 56 |不| 58 |一| 52 | 0 | 0 |
| 78 |一| 75 | 0 | 0 | 0 | 0 |
我希望能够取qty列(yr 10 qty,yr 9 qty,yr 8 qty等)的平均值,但仅当相邻列中的相关指标(yr 10 cd,yr 9 cd,yr 8 cd等)为“A”值时。如果相关指标为0或任何其他值,如“T”,我不想将其包括在平均值计算中。
我试过使用一个函数,它利用if语句将值附加到列表中,然后对非零值取平均值。然后,使用df.apply将该函数应用于df中的每一行。不幸的是,我对所有平均值都得到0,这是不期望的。
这里是预期输出。它是我的df中添加的一列,包含该行的平均值。预期输出:
| 销售|YR10CD| 10年数量|YR9CD|公司简介|YR8CD| y8qty|平均|
| --|--|--|--|--|--|--|--|
| 42 |一| 45 |一| 47 |一| 49 | 47 |
| 56 |不| 58 |一| 52 | 0 | 0 | 52 |
| 78 |一| 75 | 0 | 0 | 0 | 0 | 75 |
我已经尝试搜索堆栈溢出一段时间了,我遇到的解决方案都不适合我的特定场景。

6psbrbz9

6psbrbz91#

在某些情况下,列的结构可能是未知的,扩展到MultiIndex来执行操作可能是有用的,这允许我们利用pandas索引来确保计算期间的数据完整性。
假设一个yr[num]cdyr[num]qty配对的结构,我们可以隔离这些列并创建一个多索引,这样[num]值就在它们自己的级别中。

v = df.filter(regex='^yr\d+(cd|qty)$')
v.columns = (
    v.columns
    .str.replace(r'yr(\d+)(cd|qty)', r'\2_\1', regex=True)
    .str.split('_', expand=True)
)

字符串
在这里,我将感兴趣的列隔离到变量v中,并通过使用replacesplit重新构造列,以便cdqty的值位于级别0,数字位于级别1。
V看起来像:

cd qty cd qty cd qty
  10  10  9   9  8   8
0  A  45  A  47  A  49
1  T  58  A  52  0   0
2  A  75  0   0  0   0


注意,有很多方法可以将列重组为MultiIndex。这里有另一个示例供参考:

v.columns = (
    v.columns
    .str.split(r'(\d+)', regex=True, expand=True)
    .droplevel(0)
    .swaplevel(0, 1)
)


根据列名格式的不同,不同的方法可能更适合重构。
具有这种级别顺序的MultiIndex的主要好处是,我们可以通过访问v['cd']v['qty]来轻松访问所有cd列和qty列。
v['qty']供参考:

10   9   8
0  45  47  49
1  58  52   0
2  75   0   0


这样做的好处是,无论列的顺序如何,我们都可以可靠地对齐10,9和8之间的计算。
这允许我们过滤出whereequal to 'A' v['qty'].where(v['cd'].eq('A'))

10     9     8
0  45.0  47.0  49.0
1   NaN  52.0   NaN
2  75.0   NaN   NaN


然后在v['qty'].where(v['cd'].eq('A')).mean(axis='columns')行上取mean

0    47.0
1    52.0
2    75.0
dtype: float64


它与df具有相同的索引,因此我们可以非常简单地将值赋值回来

df['Average'] = v['qty'].where(v['cd'].eq('A')).mean(axis='columns')


df与新列:

Sales yr8cd yr9cd yr10cd  yr10qty  yr9qty  yr8qty  Average
0     42     A     A      A       45      47      49     47.0
1     56     0     A      T       58      52       0     52.0
2     78     0     0      A       75       0       0     75.0


同样,这种方法的优点是我们的初始数据列顺序并不重要。
想象一下这样一种情况,我们的cd列按升序与数字分组,我们的qty列按降序与数字分组。
| 销售|YR8CD| YR9CD| YR10CD| 10年数量|公司简介|y8qty|
| --|--|--|--|--|--|--|
| 42 |一|一|一| 45 | 47 | 49 |
| 56 | 0 |一|不| 58 | 52 | 0 |
| 78 | 0 | 0 |一| 75 | 0 | 0 |
或者,也许更现实地,想象一个场景,有人不小心把其中一列拖乱了顺序。这里概述的方法仍然会产生正确的平均值,因为计算和值过滤是基于数值而不是它们在DataFrame中的相对位置进行对齐的。

0    47.0
1    52.0
2    75.0
dtype: float64


因此,虽然这可能不是最快的解决方案或最有效的内存,但它 * 是 * 相当高的性能,同时不会牺牲索引可以提供的任何对齐完整性检查。
带有版本号的完整工作示例

import pandas as pd  # v2.1.2

df = pd.DataFrame({
    'Sales': [42, 56, 78],
    'yr10cd': ['A', 'T', 'A'],
    'yr10qty': [45, 58, 75],
    'yr9cd': ['A', 'A', '0'],
    'yr9qty': [47, 52, 0],
    'yr8cd': ['A', '0', '0'],
    'yr8qty': [49, 0, 0]
})

v = df.filter(regex='^yr\d+(cd|qty)$')
v.columns = (
    v.columns
    .str.replace(r'yr(\d+)(cd|qty)', r'\2_\1', regex=True)
    .str.split('_', expand=True)
)

df['Average'] = v['qty'].where(v['cd'].eq('A')).mean(axis='columns')

print(df)
gk7wooem

gk7wooem2#

另一个可能的解决方案:

d = (df.filter(like='cd', axis=1).eq('A') * df.filter(like='qty', axis=1).values)

df['average'] = d.where(d.ne(0)).mean(axis=1)

字符串
输出量:

Sales yr10cd  yr10qty yr9cd  yr9qty yr8cd  yr8qty  average
0     42      A       45     A      47     A      49     47.0
1     56      T       58     A      52     0       0     52.0
2     78      A       75     0       0     0       0     75.0

lxkprmvk

lxkprmvk3#

利用交替对cd/qtymask和非A,然后计算mean

tmp = df.set_index("Sales") # put aside the S

cds = tmp.iloc[:, ::2]; qty = tmp.iloc[:, 1::2]

df["average"] = (qty.mask(cds.ne("A").set_axis(qty.columns, axis=1))
                     .mean(axis=1).reset_index(drop=True))

字符串
Ouptut:

print(df)

   Sales yr10cd  yr10qty yr9cd  yr9qty yr8cd  yr8qty  average
0     42      A       45     A      47     A      49     47.0
1     56      T       58     A      52     0       0     52.0
2     78      A       75     0       0     0       0     75.0

oyxsuwqo

oyxsuwqo4#

这里有一个非常零碎的答案,应该工作:

temp_df= df.copy()
columns = list(df.columns)
for col in columns:
    if col.endswith('cd'):
        adj_col = col.replace('cd','qty') 
        temp_df[adj_col] = np.where(temp_df[col] != 'A', None, temp_df[adj_col])
    
df['average'] = temp_df[[c for c in columns if c.endswith('qty')]].mean(axis=1,skipna = True)

字符串
说明:创建一个临时的df。对于每个'cd'列,如果值不是'A',则将相邻'qty'列的值替换为None。然后,当我们对所有'qty'列进行行平均时,我们可以跳过具有None值的'qty'列。最后,当我们进行行平均时,我们同时在原始df中创建一个新列,并分配这些平均值的结果。

tsm1rwdh

tsm1rwdh5#

使用for循环方式的Row-Method:

df = pd.DataFrame(data)

def calculate_average(row):
    qty_values = []
    for i in range(10, 1, -1):
        cd_col = f'yr{i}cd'
        qty_col = f'yr{i}qty'
        if cd_col in row and qty_col in row and row[cd_col] == 'A':
            qty_values.append(row[qty_col])
    if qty_values:
        return sum(qty_values) / len(qty_values)
    else:
        return 0

df['average'] = df.apply(calculate_average, axis=1)
print(df)

字符串
输出量:

Sales yr10cd  yr10qty yr9cd  yr9qty yr8cd  yr8qty  average
0     42      A       45     A      47     A      49     47.0
1     56      T       58     A      52     0       0     52.0
2     78      A       75     0       0     0       0     75.0

矢量化方式:

df = pd.DataFrame(data)
cd_columns = df.columns[df.columns.str.contains('cd')].to_list()
qty_columns = df.columns[df.columns.str.contains('qty')].to_list()

df[qty_columns] = df[qty_columns].apply(pd.to_numeric, errors='coerce') # replace non-numeric values with NaN
avg_qty = np.where(df[cd_columns] == 'A', df[qty_columns], np.nan) # average
df['average'] = np.nanmean(avg_qty, axis=1)

print(df)


输出量:

Sales yr10cd  yr10qty yr9cd  yr9qty yr8cd  yr8qty  average
0     42      A       45     A      47     A      49     47.0
1     56      T       58     A      52     0       0     52.0
2     78      A       75     0       0     0       0     75.0

相关问题