【问题标题】:Python: Elegant way to combine output variables of a function run many timesPython:组合函数多次运行的输出变量的优雅方式
【发布时间】:2016-08-25 04:48:19
【问题描述】:

我有一个函数可以返回许多不同大小的输出数组。

arr1,arr2,arr3,arr4,arr5, ... = func(data)

我想在一个时间序列的数据上多次运行这个函数,并将每个输出变量组合成一个涵盖整个时间序列的数组。

详细说明:如果调用函数时输出 arr1 具有维度 (x,y),我想运行函数 't' 次并最终得到一个具有维度 (x,y,t) 的数组。大小为 (x,y) 的“t”数组列表也是可以接受的,但不是首选。

同样,输出数组并非都具有相同的维数,甚至不是相同的维数。 Arr2 可能有大小 (x2,y2),arr3 可能只是长度为 (x3) 的向量。我事先不知道所有这些数组的大小。

我目前的解决方案是这样的:

arr1 = []
arr2 = []
arr3 = []
...

for t in range(t_max):
   arr1_t, arr2_t, arr3_t, ... = func(data[t])

   arr1.append(arr1_t)
   arr2.append(arr2_t)
   arr3.append(arr3_t)
...

等等。然而,当每个输出数组重复 27 次时,这看起来很不雅。

有没有更好的方法来做到这一点?

【问题讨论】:

  • 那么,每个返回的数组总是具有相同的形状?例如,给定一个传入func的值,返回的arr1_t.shape总是一样的?

标签: python arrays numpy


【解决方案1】:

您可以将arr1arr2 等设为列表列表(向量或矩阵或其他)。然后使用循环迭代从func 获得的结果并将它们添加到各个列表中。

arrN = [[] for _ in range(N)]  # N being number of results from func
for t in range(t_max):
    results = func(data[t])
    for i, res in enumerate(results):
        arrN[i].append(res)

不同子列表中的元素不必具有相同的尺寸。

【讨论】:

    【解决方案2】:

    不确定它是否算“优雅”,但您可以构建结果tuples 的list,然后使用zip 按返回位置而不是按电话号码将它们分组到tuples,然后可选 map 将这些 tuples 转换为最终数据类型。比如numpyarray

    from future_builtins import map, zip  # Only on Python 2, to minimize temporaries
    import numpy as np
    
    def func(x):
         'Dumb function to return tuple of powers of x from 1 to 27'
         return tuple(x ** i for i in range(1, 28))
    
    # Example inputs for func
    data = [np.array([[x]*10]*10, dtype=np.uint8) for  in range(10)]
    
    # Output is generator of results for each call to func
    outputs = map(func, data)
    
    # Pass each complete result of func as a positional argument to zip via star
    # unpacking to regroup, so the first return from each func call is the first
    # group, then the second return the second group, etc.
    positional_groups = zip(*outputs)
    
    # Convert regrouped data (`tuple`s of 2D results) to numpy 3D result type, unpack to names
    arr1,arr2,arr3,arr4,arr5, ...,arr27 = map(np.array, positional_groups)
    

    如果在给定位置从 func 返回的元素可能具有不一致的尺寸(例如,一个调用可能会返回 10x10 作为第一个返回值,而另一个调用可能返回 5x5),您将避免最后的map 步骤(因为array不会有一致的尺寸,只需将倒数第二个步骤替换为:

    arr1,arr2,arr3,arr4,arr5, ...,arr27 = zip(*outputs)
    

    使arr# 成为二维arrays 的tuple,或者如果需要可变:

    arr1,arr2,arr3,arr4,arr5, ...,arr27 = map(list, zip(*outputs))
    

    使它们成为lists 的二维arrays。

    【讨论】:

      【解决方案3】:

      这个答案给出了一个使用structured arrays 的解决方案。它有以下要求: Ggven 一个函数 f 返回 N 数组,并且每个返回数组的大小可以不同 - 那么对于 f 的所有结果,len(array_i) 必须始终一样。例如。

      arrs_a = f("a")
      arrs_b = f("b")
      for sub_arr_a, sub_arr_b in zip(arrs_a, arrs_b):
         assert len(sub_arr_a) == len(sub_arr_b)
      

      如果上述情况属实,那么您可以使用结构化数组。结构化数组就像普通数组一样,只是具有复杂的数据类型。例如,我可以指定一个数据类型,它由一个形状为5 的整数数组和另一个形状为(2, 2) 的浮点数数组组成。例如。

      # define what a record looks like
      dtype = [
          # tuples of (field_name, data_type)
          ("a", "5i4"), # array of five 4-byte ints
          ("b", "(2,2)f8"), # 2x2 array of 8-byte floats
      ]
      

      使用dtype,您可以创建一个结构化数组,并将所有结果一次性设置在结构化数组上。

      import numpy as np
      
      def func(n):
          "mock implementation of func"
          return (
              np.ones(5) * n,
              np.ones((2,2))* n
          )
      
      # define what a record looks like
      dtype = [
          # tuples of (field_name, data_type)
          ("a", "5i4"), # array of five 4-byte ints
          ("b", "(2,2)f8"), # 2x2 array of 8-byte floats
      ]
      
      size = 5
      # create array
      arr = np.empty(size, dtype=dtype)
      # fill in values
      for i in range(size):
          # func must return a tuple
          # or you must convert the returned value to a tuple
          arr[i] = func(i)
      
      # alternate way of instantiating arr
      arr = np.fromiter((func(i) for i in range(size)), dtype=dtype, count=size)
      
      # How to use structured arrays
      # access individual record
      print(arr[1]) # prints ([1, 1, 1, 1, 1], [[1, 1], [1, 1]])
      # access specific value -- get second record -> get b field -> get value at 0,0
      assert arr[2]['b'][0,0] == 2
      # access all values of a specific field
      print(arr['a']) # prints all the a arrays
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-01-18
        • 2021-12-19
        • 2020-06-01
        • 1970-01-01
        • 2012-12-24
        • 2011-07-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多