【问题标题】:numpy - how to outer join arraysnumpy - 如何外连接数组
【发布时间】:2014-05-06 17:05:48
【问题描述】:

我正在尝试将这三个数组合并到下面的数组中。基本上相当于 SQL 外连接(其中 'pos' 字段是键/索引)

a1 = array([('2:6506', 4.6725971801473496e-25, 0.99999999995088695),
       ('2:6601', 2.2452745388799898e-27, 0.99999999995270605),
       ('2:21801', 1.9849650921836601e-31, 0.99999999997999001),], 
      dtype=[('pos', '|S100'), ('col1', '<f8'), ('col2', '<f8')])

a2 = array([('3:6506', 4.6725971801473496e-25, 0.99999999995088695),
       ('3:6601', 2.2452745388799898e-27, 0.99999999995270605),
       ('3:21801', 1.9849650921836601e-31, 0.99999999997999001),], 
      dtype=[('pos', '|S100'), ('col1', '<f8'), ('col2', '<f8')])

a3 = array([('2:6506', 4.6725971801473496e-25, 0.99999999995088695),
       ('2:6601', 2.2452745388799898e-27, 0.99999999995270605),
       ('2:21801', 1.9849650921836601e-31, 0.99999999997999001),], 
      dtype=[('pos', '|S100'), ('col3', '<f8'), ('col4', '<f8')])

想要的结果:

array([('2:6506', 4.6725971801473496e-25, 0.99999999995088695, 4.6725971801473496e-25, 0.99999999995088695),
       ('2:6601', 2.2452745388799898e-27, 0.99999999995270605, 2.2452745388799898e-27, 0.99999999995270605),
       ('2:21801', 1.9849650921836601e-31, 0.99999999997999001, 1.9849650921836601e-31, 0.99999999997999001),
       ('3:6506', 4.6725971801473496e-25, 0.99999999995088695, NaN, NaN),
       ('3:6601', 2.2452745388799898e-27, 0.99999999995270605, NaN, NaN),
       ('3:21801', 1.9849650921836601e-31, 0.99999999997999001, NaN, NaN),
        ], 
      dtype=[('pos', '|S100'), ('col1', '<f8'), ('col2', '<f8'), ('col3', '<f8'), ('col4', '<f8')])

我认为this answer 可能是在正确的轨道上,我只是不太明白如何应用它。

更新:

我尝试运行 unutbu 的答案,但出现此错误:

Traceback (most recent call last):
  File "fail2.py", line 21, in <module>
    a4 = recfunctions.join_by('pos', a4, a, jointype='outer')
  File "/usr/local/msg/lib/python2.6/site-packages/numpy/lib/recfunctions.py", line 973, in join_by
    current = output[f]
  File "/usr/local/msg/lib/python2.6/site-packages/numpy/ma/core.py", line 2943, in __getitem__
    dout = ndarray.__getitem__(_data, indx)
ValueError: field named col12 not found.

更新 2

我只在 numpy 1.5.1 上遇到此错误。我升级到 1.8.1 后它消失了。

【问题讨论】:

  • 你会发布每个数组的 dtypes 吗?
  • 嗯,我刚刚从你的答案中运行了代码。那不是指定dtypes吗?或者你认为后来有什么东西改变了他们?

标签: python numpy


【解决方案1】:
import numpy as np
import numpy.lib.recfunctions as recfunctions

a1 = np.array([('2:6506', 4.6725971801473496e-25, 0.99999999995088695),
       ('2:6601', 2.2452745388799898e-27, 0.99999999995270605),
       ('2:21801', 1.9849650921836601e-31, 0.99999999997999001),], 
      dtype=[('pos', '|S100'), ('col1', '<f8'), ('col2', '<f8')])

a2 = np.array([('3:6506', 4.6725971801473496e-25, 0.99999999995088695),
       ('3:6601', 2.2452745388799898e-27, 0.99999999995270605),
       ('3:21801', 1.9849650921836601e-31, 0.99999999997999001),], 
      dtype=[('pos', '|S100'), ('col1', '<f8'), ('col2', '<f8')])

a3 = np.array([('2:6506', 4.6725971801473496e-25, 0.99999999995088695),
       ('2:6601', 2.2452745388799898e-27, 0.99999999995270605),
       ('2:21801', 1.9849650921836601e-31, 0.99999999997999001),], 
      dtype=[('pos', '|S100'), ('col3', '<f8'), ('col4', '<f8')])

result = a1
for a in (a2, a3):
    cols = list(set(result.dtype.names).intersection(a.dtype.names))
    result = recfunctions.join_by(cols, result, a, jointype='outer')
print(result)

产量

[ ('2:21801', 1.98496509218366e-31, 0.99999999997999, 1.98496509218366e-31, 0.99999999997999)
 ('2:6506', 4.67259718014735e-25, 0.999999999950887, 4.67259718014735e-25, 0.999999999950887)
 ('2:6601', 2.24527453887999e-27, 0.999999999952706, 2.24527453887999e-27, 0.999999999952706)
 ('3:21801', 1.98496509218366e-31, 0.99999999997999, --, --)
 ('3:6506', 4.67259718014735e-25, 0.999999999950887, --, --)
 ('3:6601', 2.24527453887999e-27, 0.999999999952706, --, --)]

如果您在 NumPy 数组上执行类似 SQL 的连接,您可能需要考虑使用 Pandas。 Pandas 基于 NumPy 构建,提供了更丰富的数据处理函数:

import numpy as np
import pandas as pd
a1 = np.array([('2:6506', 4.6725971801473496e-25, 0.99999999995088695),
       ('2:6601', 2.2452745388799898e-27, 0.99999999995270605),
       ('2:21801', 1.9849650921836601e-31, 0.99999999997999001),], 
      dtype=[('pos', '|S100'), ('col1', '<f8'), ('col2', '<f8')])

a2 = np.array([('3:6506', 4.6725971801473496e-25, 0.99999999995088695),
       ('3:6601', 2.2452745388799898e-27, 0.99999999995270605),
       ('3:21801', 1.9849650921836601e-31, 0.99999999997999001),], 
      dtype=[('pos', '|S100'), ('col1', '<f8'), ('col2', '<f8')])

a3 = np.array([('2:6506', 4.6725971801473496e-25, 0.99999999995088695),
       ('2:6601', 2.2452745388799898e-27, 0.99999999995270605),
       ('2:21801', 1.9849650921836601e-31, 0.99999999997999001),], 
      dtype=[('pos', '|S100'), ('col3', '<f8'), ('col4', '<f8')])

dfs = [pd.DataFrame.from_records(a) for a in (a1, a2, a3)]

result = dfs[0]
for df in dfs[1:]:
    cols = list(set(result.columns).intersection(df.columns))
    result = pd.merge(result, df, how='outer', left_on=cols, right_on=cols)

print(result)

产量

       pos          col1  col2          col3  col4
0   2:6506  4.672597e-25     1  4.672597e-25     1
1   2:6601  2.245275e-27     1  2.245275e-27     1
2  2:21801  1.984965e-31     1  1.984965e-31     1
3   3:6506  4.672597e-25     1           NaN   NaN
4   3:6601  2.245275e-27     1           NaN   NaN
5  3:21801  1.984965e-31     1           NaN   NaN

[6 rows x 5 columns]

有时 Pandas 可能比纯 NumPy 解决方案慢一些。但这通常是因为 Pandas 提供了更强大的解决方案,可以正确处理诸如 NaN 或重复索引值之类的极端情况——即席 NumPy 解决方案可能无法解决的问题。

另请注意,Pandas DataFrames 有一个 .values 属性,它返回一个 NumPy 基础数据数组,以及一个 .to_records 方法,它返回一个结构化数组。正如您在上面看到的,有一个Dataframe.from_records 构造函数将结构化数组转换为DataFrame。所以如果你真的需要的话,你可以很容易地在 DataFrames 和 NumPy 数组之间移动。

所以我认为使用 Pandas 并没有任何真正的速度劣势,它提供的便利应该让您可以更轻松地进行更多数据分析。

【讨论】:

  • 看来应该可以了。但我遇到了一个错误。我用错误更新了问题,因为我无法在这里格式化。
  • 关于熊猫的好主意。会不会有超速处罚?原来的 numpy 方法有什么根本错误吗?
  • 不,使用 NumPy 结构化数组从根本上说没有错,但我必须说,我发现使用 Pandas DataFrames 是一种乐趣,而使用 NumPy 结构化数组做某些事情似乎更像是一场战斗。例如,没有简单的方法来子选择列。
  • 抱歉,我不知道为什么您在运行我发布的代码时会看到错误。 :( 我使用的是 Python2.7,不是 2.6,但是我找不到任何相关的错误报告,所以版本差异可能不是问题。
  • 我升级到 1.8.1 并且可以正常工作!感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 2017-05-27
  • 1970-01-01
  • 1970-01-01
  • 2016-05-20
  • 1970-01-01
  • 2012-04-04
  • 2013-08-22
相关资源
最近更新 更多