python - 如何使用 Pandas 处理行对并在没有字典的情况下保留 ID 列?

标签 python pandas

我正在寻找更好的解决方案来解决我在研究中遇到的数据处理问题。我有一个 Pandas DataFrame,我试图提取组(Frame)中特定列(Z)的所有值,保留不同列(名称)给出的 ID 对。最终结果不必再是 Pandas 对象,但最好完全在 Pandas 中完成此操作。通过示例可以最清楚地了解任务。

d=[['7500', '3.2900', '0', 'apple'],['7500', '-0.3500', '1', 'orange'],['7500', '-4.1400', '2', 'orange'],['7501', '3.4625', '0', 'apple'],['7501', '-0.2275', '1', 'apple'],['7501', '-4.1175', '2', 'orange'],['7502', '3.2087', '0', 'orange'],['7502', '-0.7313', '1', 'apple'],['7502', '-4.7513', '2', 'apple']]
df=pd.DataFrame(d, columns=["Frame","Z","Order","Name"])


>>> df
    Frame       Z   Order   Name
0   7500     3.2900 0   apple
1   7500    -0.3500 1   orange
2   7500    -4.1400 2   orange
3   7501     3.4625 0   apple
4   7501    -0.2275 1   apple
5   7501    -4.1175 2   orange
6   7502     3.2087 0   orange
7   7502    -0.7313 1   apple
8   7502    -4.7513 2   apple

因此,对于每个框架组,我想根据“顺序”列采用独特的组合,因此对于框架 7500 组,这将是:

(0,1)
(0,2)
(1,2) 

但请注意,每组中的行数可以在 1 到 5 之间变化,而不仅仅是 0、1、2。然后我会跟踪这些对的“名称”值,

(apple, orange)
(apple, orange)
(orange, orange)

然后我会取出这些相应对的“Z”值,如下所示:

( 3.2900, -0.3500)
( 3.2900, -4.1400)
(-0.3500, -4.1400)

现在,最后,结合这些数据,我想要基于“名称”列的每个唯一对的两个列表。在本例中,我们将这些列表称为first_vals和second_vals,但这完全是任意的,

(apple, orange)
first_vals = [3.2900, 3.2900, ...]
second_vals = [-0.3500, -4.1400, ...]

(orange, orange)
first_vals = [-0.3500, ...]
second_vals = [-4.140, ...]

这是我使用字典想出的一个解决方案。它有效,但我认为它非常丑陋,并且隐藏了字典键中的数据结构。此示例依赖于按 Order 列对组进行预排序,但这实际上没有问题。

from itertools import combinations
from collections import defaultdict

zpairs = defaultdict(list)

for name, group in df.groupby(["Frame"]):
    order_pairs = combinations(range(len(group)), 2)
    zvals = group["Z"].values
    rowids = group["Name"].values
    for pair in order_pairs:
        pair_str = str(rowids[pair[0]])+"-"+str(rowids[pair[1]])
        zpairs[pair_str+"-first"].append(zvals[pair[0]])
        zpairs[pair_str+"-second"].append(zvals[pair[1]])

该代码的结果如下所示:

>>> dict(zpairs)

{'apple-apple-first': ['3.4625', '-0.7313'],
'apple-apple-second': ['-0.2275', '-4.7513'],
'apple-orange-first': ['3.2900', '3.2900', '3.4625', '-0.2275'],
'apple-orange-second': ['-0.3500', '-4.1400', '-4.1175', '-4.1175'],
'orange-apple-first': ['3.2087', '3.2087'],
'orange-apple-second': ['-0.7313', '-4.7513'],
'orange-orange-first': ['-0.3500'],
'orange-orange-second': ['-4.1400']}

有没有一种方法可以处理我的数据框,不依赖字典,也不使用字典键来存储数据?这实际上并不是为了提高性能,但这会很有帮助。

最佳答案

这是一个分两步完成的 pandas 方法:

  1. 获取每个框架组的名称和 Z 值对。
  2. 合并每对名称的 Z 值分量。

第 1 步

为了获取名称和 z 值对,我将编写一个辅助函数 get_group_pairs,我将在执行 groupby 时调用该函数。我正在执行与您在循环中执行的操作类似的过程,但在 DataFrame 中返回输出:

def get_group_pairs(grp):
    pairs = combinations(grp.index, 2)
    data = [grp.loc[p, ('Name', 'Z')].values.flatten('F') for p in pairs]
    return pd.DataFrame(data, columns=['Name1', 'Name2', 'Z1', 'Z2'])

namepairs = df.groupby('Frame').apply(get_group_pairs).reset_index(level=1, drop=True)

reset_index纯粹是为了删除不必要的索引级别,以便中间输出看起来不错。如果您不关心中间输出,则没有必要。中间输出namepairs:

        Name1   Name2       Z1       Z2
Frame                                  
7500    apple  orange   3.2900  -0.3500
7500    apple  orange   3.2900  -4.1400
7500   orange  orange  -0.3500  -4.1400
7501    apple   apple   3.4625  -0.2275
7501    apple  orange   3.4625  -4.1175
7501    apple  orange  -0.2275  -4.1175
7502   orange   apple   3.2087  -0.7313
7502   orange   apple   3.2087  -4.7513
7502    apple   apple  -0.7313  -4.7513

第 2 步

与步骤 1 中相同的辅助函数/groupby/apply 模式。本质上,我只是使用两个名称进行分组,然后将两个 Z 列转换为列表:

def merge_zpairs(grp):
    data = {'Z1': grp['Z1'].tolist(), 'Z2': grp['Z2'].tolist()}
    return pd.Series(data)

zpairs = namepairs.groupby(['Name1', 'Name2']).apply(merge_zpairs).reset_index()

同样,reset_index 并不是绝对必要的。如果没有它,您将得到一个由名称对组成的 MultiIndex。这将产生最终输出 zpairs:

    Name1   Name2                                 Z1                                    Z2
0   apple   apple                  [3.4625, -0.7313]                    [-0.2275, -4.7513]
1   apple  orange  [3.2900, 3.2900, 3.4625, -0.2275]  [-0.3500, -4.1400, -4.1175, -4.1175]
2  orange   apple                   [3.2087, 3.2087]                    [-0.7313, -4.7513]
3  orange  orange                          [-0.3500]                             [-4.1400]

组合代码

为方便起见,以下是步骤 1 和 2 中组合的代码:

def get_group_pairs(grp):
    pairs = combinations(grp.index, 2)
    data = [grp.loc[p, ('Name', 'Z')].values.flatten('F') for p in pairs]
    return pd.DataFrame(data, columns=['Name1', 'Name2', 'Z1', 'Z2'])

def merge_zpairs(grp):
    data = {'Z1': grp['Z1'].tolist(), 'Z2': grp['Z2'].tolist()}
    return pd.Series(data)

namepairs = df.groupby('Frame').apply(get_group_pairs).reset_index(level=1, drop=True)
zpairs = namepairs.groupby(['Name1', 'Name2']).apply(merge_zpairs).reset_index()

获取字典

通过对步骤 2 的代码进行一些细微修改,您可以获得与您的类似的字典结构,尽管并不完全相同。本质上,不要使用 reset_index,而是使用 to_dictorient='index':

zpairs = namepairs.groupby(['Name1', 'Name2']).apply(merge_zpairs)
zpairs_dict = zpairs.to_dict(orient='index')

这会产生一个字典的字典:第一个键是一对名称,第二个键是您想要的 Z 值。例如,'apple-apple-first'的语法为:

zpairs_dict[('apple', 'apple')]['Z1']

关于python - 如何使用 Pandas 处理行对并在没有字典的情况下保留 ID 列?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37554460/

相关文章:

python - 使用 Python 进行合并排序

Python:如何检查字符串中的内容是否正确

python - 根据 2 个唯一列对数据集重新采样

python - 如何正确设置 pandas.Dataframe 中特定单元格的值?

python - 使用python计算tsv文件的列中单词的出现次数

python - NetworkX - 保持边缘秩序

python - 是否可以在 Jupyter 笔记本中显示控制台?

python - 将彼此时间范围内的任意日期对象组合在一起

python - 将每日 OHLCV 重新采样为每周 OHLCV

python - 如何将3d数组转换为数据框,以使第三维成为该数据框的列表项