【问题标题】:Calculating the size of a full outer join in pandas计算 pandas 中全外连接的大小
【发布时间】:2016-12-12 21:01:18
【问题描述】:

tl;博士

我的问题是,当使用 Pandas DataFrames 作为组合图的一部分时,我一直在计算在完全外部合并的每个部分上预期有多少行。

问题(下文重复)。

  1. 理想的解决方案是不需要合并并查询panel 对象。鉴于panel 上没有查询方法,是否有更简洁的解决方案可以在不达到内存上限的情况下解决此问题?
  2. 如果对 2 的回答是否定的,我如何计算每个集合组合所需的合并表的大小而不执行合并?这可能是一种次优方法,但在这种情况下,它对于应用程序的目的来说是可以接受的。
  3. Python 是否适合这种情况,或者我应该寻找一种更具统计性的语言,例如R 还是在较低级别编写它(ccython) - 数据库不在问题。

问题

最近我重新编写了py-upset graphing library,以便在计算跨 DataFrame 的组合时在时间方面更有效。我不是在寻找对此代码的审查,它在大多数情况下都运行良好,我对这种方法感到满意。我现在正在寻找的是一个非常具体的问题的答案;在处理大型数据集时发现。

我在重写时采用的方法是在内存中合并所有提供的数据帧,如480 - 502 of pyupset.resources 行所示的完全外部连接

        for index, key in enumerate(keys):
            frame = self._frames[key]
            frame.columns = [
                '{0}_{1}'.format(column, key)
                if column not in self._unique_keys
                else
                column
                for column in self._frames[key].columns
            ]
            if index == 0:
                self._merge = frame
            else:
                suffixes = (
                    '_{0}'.format(keys[index-1]),
                    '_{0}'.format(keys[index]),
                )
                self._merge = self._merge.merge(
                    frame,
                    on=self._unique_keys,
                    how='outer',
                    copy=False,
                    suffixes=suffixes
                )

对于中小型数据帧,使用连接的效果非常好。事实上,最近的性能测试表明,它可以在不到一分钟的时间内处理 5 或 6 个数据集,每个数据集包含 10,000 行,这对于我需要的应用程序结构来说绰绰有余。

问题现在从基于时间转移到基于内存。

考虑到可能包含数千条记录的数据集,即使在大型服务器上,该库也会很快耗尽内存。

为了说明这一点,我的这个应用程序的测试机器是一个 8 核 VMWare 机器,具有 128GiB RAM,运行 Centos7。

鉴于以下数据集大小,当添加第 5 个数据帧时,内存使用量呈指数增长。这是非常出乎意料的,但强调了我面临的问题的核心。

  Rows | Dataframe
------------------------
 13963 | dataframe_one
 48346 | dataframe_two
 52356 | dataframe_three
337292 | dataframe_four
 49936 | dataframe_five
 24542 | dataframe_six
258093 | dataframe_seven
 16337 | dataframe_eight

就行数而言,这些不是“”数据帧,尽管每个数据帧的列数限制为一个唯一键 + 4 个非唯一列。 pandas中每一列的大小为

column | type     | unique
--------------------------
X      | object   | Y
id     | int64    | N
A      | float64  | N
B      | float64  | N
C      | float64  | N

此合并可能会导致内存被耗尽。有时它会因 MemoryError 而中止(太好了,我可以捕捉并处理这些错误),有时内核会接管并在系统变得不稳定之前简单地杀死应用程序,有时,系统会挂起并变得无响应/不稳定,直到最终内核杀死应用程序并释放内存。

样本输出(内存大小近似):

[INFO] Creating merge table
[INFO] Merging table dataframe_one
[INFO] Data index length = 13963     # approx memory <500MiB
[INFO] Merging table dataframe_two
[INFO] Data index length = 98165     # approx memory <1.8GiB
[INFO] Merging table dataframe_three
[INFO] Data index length = 1296665   # approx memory <3.0GiB
[INFO] Merging table dataframe_four
[INFO] Data index length = 244776542 # approx memory ~13GiB
[INFO] Merging table dataframe_five
Killed # > 128GiB

当合并表产生后,以集合组合方式查询,产生类似https://github.com/mproffitt/py-upset/blob/feature/ISSUE-7-Severe-Performance-Degradation/tests/generated/extra_additional_pickle.png的图

我为解决内存问题而尝试构建的方法是查看为合并提供的集合,预先确定合并需要多少内存,然后如果该组合需要太多,则将其拆分为更小的组合,分别计算每一个,然后将最终的数据帧重新组合在一起(分而治之)。

我的问题是我一直在计算合并的每个部分预期的行数。

问题(从上面重复)

  1. 理想的解决方案是不需要合并并查询panel 对象。鉴于panel 上没有查询方法,是否有更简洁的解决方案可以在不达到内存上限的情况下解决此问题?
  2. 如果对 2 的回答是否定的,我如何计算每个集合组合所需的合并表的大小而不执行合并?这可能是一种次优方法,但在这种情况下,它对于应用程序的目的来说是可以接受的。
  3. Python 是否适合这方面的语言,或者我应该寻找一种更具统计性的语言,例如 R 还是在较低级别编写它(ccython)。

对冗长的问题表示歉意。如果需要或可能,我很乐意提供更多信息。

谁能解释一下这可能是什么原因?

谢谢。

【问题讨论】:

    标签: python-3.x pandas merge combinatorics


    【解决方案1】:

    问题 1。

    Dask 通过使用 hdf5 文件作为临时存储来计算“内存不足”的合并表显示了很多希望。

    通过使用多处理来创建合并,dask 还提供了超过pandas 的性能提升。不幸的是,query 方法没有实现这一点,因此在合并中获得的性能提升在查询时会丢失。

    这仍然不是一个完全可行的解决方案,因为 dask 在大型、复杂的合并中可能仍然会耗尽内存。

    问题 2。

    使用以下方法完全可以预先计算合并的大小。

    1. 按唯一键对每个数据帧进行分组并计算大小。
    2. 为每个数据框创建一组键名。
    3. 从 2 创建集合的交集。
    4. 为集 1 和集 2 创建集差异
    5. 要适应存储在唯一键中的np.nan,请选择所有 NAN 值。如果一帧包含 nan 而另一帧不包含,则将另一帧写为 1。
    6. 对于交叉点中的集合,将每个 groupby('...').size() 的计数相乘
    7. 从设置的差异中添加计数
    8. 添加 np.nan 值的计数

    在python中可以写成:

    def merge_size(left_frame, right_frame, group_by):
        left_groups = left_frame.groupby(group_by).size()
        right_groups = right_frame.groupby(group_by).size()
        left_keys = set(left_groups.index)
        right_keys = set(right_groups.index)
        intersection = right_keys & left_keys
        left_sub_right = left_keys - intersection
        right_sub_left = right_keys - intersection
    
        left_nan = len(left_frame.query('{0} != {0}'.format(group_by)))
        right_nan = len(right_frame.query('{0} != {0}'.format(group_by)))
        left_nan = 1 if left_nan == 0 and right_nan != 0 else left_nan
        right_nan = 1 if right_nan == 0 and left_nan != 0 else right_nan
    
        sizes = [(left_groups[group_name] * right_groups[group_name]) for group_name in intersection]
        sizes += [left_groups[group_name] for group_name in left_sub_right]
        sizes += [right_groups[group_name] for group_name in right_sub_left]
        sizes += [left_nan * right_nan]
        return sum(sizes) 
    

    问题 3

    这种方法计算量很大,最好写成Cython来提高性能。

    【讨论】:

      猜你喜欢
      • 2019-10-28
      • 2019-12-13
      • 1970-01-01
      • 1970-01-01
      • 2022-06-11
      • 1970-01-01
      • 2020-09-27
      • 2023-04-03
      • 2017-02-04
      相关资源
      最近更新 更多