Python,pandas:如何从对称的多索引数据框中提取值

Posted

技术标签:

【中文标题】Python,pandas:如何从对称的多索引数据框中提取值【英文标题】:Python, pandas: how to extract values from a symmetric, multi-index dataframe 【发布时间】:2016-12-22 02:29:20 【问题描述】:

我有一个对称的多索引数据框,我想从中系统地提取数据:

import pandas as pd

df_index = pd.MultiIndex.from_arrays(
    [["A", "A", "B", "B"], [1, 2, 3, 4]], names = ["group", "id"])
df = pd.DataFrame(
    [[1.0, 0.5, 0.3, -0.4],
     [0.5, 1.0, 0.9, -0.8],
     [0.3, 0.9, 1.0, 0.1],
     [-0.4, -0.8, 0.1, 1.0]],
    index=df_index, columns=df_index)

我想要一个函数extract_vals,它可以返回与同一组中的元素相关的所有值,除了对角线 AND 元素不能重复计算。以下是所需行为的两个示例(顺序无关紧要):

A_vals = extract_vals("A", df) # [0.5, 0.3, -0.4, 0.9, -0.8]
B_vals = extract_vals("B", df) # [0.3, 0.9, 0.1, -0.4, -0.8]

我的问题类似于this question on SO,但我的情况不同,因为我使用的是多索引数据框。

最后,为了让事情更有趣,请考虑效率,因为我将在更大的数据帧上运行多次。非常感谢!

编辑:

Happy001 的解决方案很棒。我自己提出了一种方法,基于提取目标不在行和列中的元素的逻辑,然后提取目标在行和列中都存在的元素的下三角形。但是,Happy001 的解决方案要快得多。

首先,我创建了一个更复杂的数据框,以确保这两种方法都是通用的:

import pandas as pd
import numpy as np

df_index = pd.MultiIndex.from_arrays(
    [["A", "B", "A", "B", "C", "C"], [1, 2, 3, 4, 5, 6]], names=["group", "id"])
df = pd.DataFrame(
    [[1.0, 0.5, 1.0, -0.4, 1.1, -0.6],
     [0.5, 1.0, 1.2, -0.8, -0.9, 0.4],
     [1.0, 1.2, 1.0, 0.1, 0.3, 1.3],
     [-0.4, -0.8, 0.1, 1.0, 0.5, -0.2],
     [1.1, -0.9, 0.3, 0.5, 1.0, 0.7],
     [-0.6, 0.4, 1.3, -0.2, 0.7, 1.0]],
    index=df_index, columns=df_index)

接下来,我定义了两个版本的 extract_vals(第一个是我自己的):

def extract_vals(target, multi_index_level_name, df):
    # Extract entries where target is in the rows but NOT also in the columns
    target_in_rows_but_not_in_cols_vals = df.loc[
        df.index.get_level_values(multi_index_level_name) == target,
        df.columns.get_level_values(multi_index_level_name) != target]

    # Extract entries where target is in the rows AND in the columns
    target_in_rows_and_cols_df = df.loc[
        df.index.get_level_values(multi_index_level_name) == target,
        df.columns.get_level_values(multi_index_level_name) == target]
    mask = np.triu(np.ones(target_in_rows_and_cols_df.shape), k = 1).astype(np.bool)
    vals_with_nans = target_in_rows_and_cols_df.where(mask).values.flatten()
    target_in_rows_and_cols_vals = vals_with_nans[~np.isnan(vals_with_nans)]

    # Append both arrays of extracted values
    vals = np.append(target_in_rows_but_not_in_cols_vals, target_in_rows_and_cols_vals)

    return vals

def extract_vals2(target, multi_index_level_name, df):
    # Get indices for what you want to extract and then extract all at once
    coord = [[i, j] for i in range(len(df)) for j in range(len(df)) if i < j and (
        df.index.get_level_values(multi_index_level_name)[i] == target or (
            df.columns.get_level_values(multi_index_level_name)[j] == target))]

    return df.values[tuple(np.transpose(coord))]

我检查了两个函数是否都返回了所需的输出:

# Expected values
e_A_vals = np.sort([0.5, 1.0, -0.4, 1.1, -0.6, 1.2, 0.1, 0.3, 1.3])
e_B_vals = np.sort([0.5, 1.2, -0.8, -0.9, 0.4, -0.4, 0.1, 0.5, -0.2])
e_C_vals = np.sort([1.1, -0.9, 0.3, 0.5, 0.7, -0.6, 0.4, 1.3, -0.2])

# Sort because order doesn't matter
assert np.allclose(np.sort(extract_vals("A", "group", df)), e_A_vals)
assert np.allclose(np.sort(extract_vals("B", "group", df)), e_B_vals)
assert np.allclose(np.sort(extract_vals("C", "group", df)), e_C_vals)

assert np.allclose(np.sort(extract_vals2("A", "group", df)), e_A_vals)
assert np.allclose(np.sort(extract_vals2("B", "group", df)), e_B_vals)
assert np.allclose(np.sort(extract_vals2("C", "group", df)), e_C_vals)

最后,我检查了速度:

## Test speed
import time

# Method 1
start1 = time.time()
for ii in range(10000):
    out = extract_vals("C", "group", df)
elapsed1 = time.time() - start1
print elapsed1 # 28.5 sec

# Method 2
start2 = time.time()
for ii in range(10000):
    out2 = extract_vals2("C", "group", df)
elapsed2 = time.time() - start2
print elapsed2 # 10.9 sec

【问题讨论】:

你能展示一下你的尝试吗? 我在最近的编辑中包含了我的解决方案。希望对您有所帮助,@Merlin。 【参考方案1】:

这就是你想要的吗?

对角线以上的所有元素:

In [139]: df.values[np.triu_indices(len(df), 1)]
Out[139]: array([ 0.5,  0.3, -0.4,  0.9, -0.8,  0.1])

A_vals:

In [140]: df.values[np.triu_indices(len(df), 1)][:-1]
Out[140]: array([ 0.5,  0.3, -0.4,  0.9, -0.8])

B_vals:

In [141]: df.values[np.triu_indices(len(df), 1)][1:]
Out[141]: array([ 0.3, -0.4,  0.9, -0.8,  0.1])

源矩阵:

In [142]: df.values
Out[142]:
array([[ 1. ,  0.5,  0.3, -0.4],
       [ 0.5,  1. ,  0.9, -0.8],
       [ 0.3,  0.9,  1. ,  0.1],
       [-0.4, -0.8,  0.1,  1. ]])

【讨论】:

这不是通用功能,因为您必须在 tril 和 triu 之间切换,对吗?此外,如果我想保留的数据点之一是 0,那么通过这个实现,我会丢失它。不过,我不知道您可以使用点符号访问数据;这很整洁。 .values 为您提供numpy 数组在pandas DataFrame 后面使用。【参考方案2】:

我不认为df 具有相同的列和索引。 (当然它们可以相同)。

def extract_vals(group_label, df):    
    coord = [[i, j] for i in range(len(df)) for j in range(len(df)) if i<j and (df.index.get_level_values('group')[i] == group_label or df.columns.get_level_values('group')[j] == group_label) ]
    return df.values[tuple(np.transpose(coord))]

print extract_vals('A', df)
print extract_vals('B', df)

结果:

[ 0.5  0.3 -0.4  0.9 -0.8]
[ 0.3 -0.4  0.9 -0.8  0.1]

【讨论】:

以上是关于Python,pandas:如何从对称的多索引数据框中提取值的主要内容,如果未能解决你的问题,请参考以下文章

Python Pandas:如何添加另一个名称的多索引?

如何将带有元组键的 python 字典转换为 pandas 多索引数据框?

从 Pandas 多索引数据框创建等高线图

Pandas 多索引数据框 - 从多索引中的一个索引中选择最大值

在多个时间范围内聚合/重采样 pandas 多索引数据帧并预测 ARIMA

使用 Python/Pandas 将多索引数据写入 excel 文件