【问题标题】:numpy: How to add a column to an existing structured array?numpy:如何将一列添加到现有的结构化数组中?
【发布时间】:2014-10-15 03:18:33
【问题描述】:

我有一个起始数组,例如:

[(1, [-112.01268501699997, 40.64249414272372])
 (2, [-111.86145708699996, 40.4945008710162])]

第一列是int,第二列是floats 中的list。我需要添加一个名为'USNG' 的str 列。

然后我创建一个结构化的 numpy 数组,如下所示:

dtype = numpy.dtype([('USNG', '|S100')])
x = numpy.empty(array.shape, dtype=dtype)

我想将x numpy 数组作为新列附加到现有数组中,这样我就可以为每一行向该列输出一些信息。

当我执行以下操作时:

numpy.append(array, x, axis=1)

我收到以下错误:

'TypeError: invalid type promotion'

我也试过vstack和hstack

【问题讨论】:

    标签: python python-2.7 numpy structured-array recarray


    【解决方案1】:

    您必须创建一个包含新字段的新 dtype。

    例如,这里是a:

    In [86]: a
    Out[86]: 
    array([(1, [-112.01268501699997, 40.64249414272372]),
           (2, [-111.86145708699996, 40.4945008710162])], 
          dtype=[('i', '<i8'), ('loc', '<f8', (2,))])
    

    a.dtype.descr 是[('i', '&lt;i8'), ('loc', '&lt;f8', (2,))];即字段类型的列表。我们将通过将('USNG', 'S100') 添加到该列表的末尾来创建一个新的dtype:

    In [87]: new_dt = np.dtype(a.dtype.descr + [('USNG', 'S100')])
    

    现在创建一个新结构化数组b。我在这里使用了zeros,因此字符串字段将以值'' 开头。您也可以使用empty。然后这些字符串将包含垃圾,但如果您立即为它们分配值,那将无关紧要。

    In [88]: b = np.zeros(a.shape, dtype=new_dt)
    

    将现有数据从a复制到b:

    In [89]: b['i'] = a['i']
    
    In [90]: b['loc'] = a['loc']
    

    现在是b:

    In [91]: b
    Out[91]: 
    array([(1, [-112.01268501699997, 40.64249414272372], ''),
           (2, [-111.86145708699996, 40.4945008710162], '')], 
          dtype=[('i', '<i8'), ('loc', '<f8', (2,)), ('USNG', 'S100')])
    

    用一些数据填写新字段:

    In [93]: b['USNG'] = ['FOO', 'BAR']
    
    In [94]: b
    Out[94]: 
    array([(1, [-112.01268501699997, 40.64249414272372], 'FOO'),
           (2, [-111.86145708699996, 40.4945008710162], 'BAR')], 
          dtype=[('i', '<i8'), ('loc', '<f8', (2,)), ('USNG', 'S100')])
    

    【讨论】:

    • 一个性能问题,如果你对数组的数据更新做一个for循环,你会把函数向量化吗?
    • 这取决于您正在执行的更新类型,以及您将如何对其进行矢量化。开始一个新问题而不是在这些 cmets 中讨论它会更有效。
    【解决方案2】:

    问题恰恰是:“关于为什么会发生这种情况的任何建议?”

    基本上,这是bug--- 自 2012 年以来,它一直是 numpy 的公开票。

    【讨论】:

      【解决方案3】:

      你试过用numpy的recfunctions吗?

      import numpy.lib.recfunctions as rfn
      

      它对结构化数组有一些非常有用的功能。

      对于您的情况,我认为可以通过以下方式完成:

      a = rfn.append_fields(a, 'USNG', np.empty(a.shape[0], dtype='|S100'), dtypes='|S100')
      

      在这里测试过,效果很好。


      合并数组

      正如 cmets 中提到的 GMSL。可以使用 rfn.merge_arrays 来做到这一点,如下所示:

      a = np.array([(1, [-112.01268501699997, 40.64249414272372]),
             (2, [-111.86145708699996, 40.4945008710162])], 
            dtype=[('i', '<i8'), ('loc', '<f8', (2,))])
      a2 = np.full(a.shape[0], '', dtype=[('USNG', '|S100')])
      a3 = rfn.merge_arrays((a, a2), flatten=True)
      

      a3 将具有值:

      array([(1, [-112.01268502,   40.64249414], b''),
             (2, [-111.86145709,   40.49450087], b'')],
            dtype=[('i', '<i8'), ('loc', '<f8', (2,)), ('USNG', 'S100')])
      

      【讨论】:

      • recfunction 函数merge_arrays() 在这种情况下会更简单。
      • 酷,不知道。需要进行一些修改才能发现需要 flatten 参数才能给出适当的行为:)。相应地编辑了答案。
      【解决方案4】:
      • 如果pandas 是一个选项,则可以更轻松地将列添加到recarray。
      1. 使用pandas.DataFrame 或pandas.DataFrame.from_records 读取当前的recarray。
      2. 将新的数据列添加到数据框中
      3. 使用pandas.DataFrame.to_records 将数据框导出到recarray
      import pandas as pd
      import numpy as np
      
      # current recarray
      data = np.rec.array([(1, list([-112.01268501699997, 40.64249414272372])), (2, list([-111.86145708699996, 40.4945008710162]))], dtype=[('i', '<i8'), ('loc', 'O')])
      
      # create dataframe
      df = pd.DataFrame(data)
      
      # display(df)
         i                                       loc
      0  1  [-112.01268501699997, 40.64249414272372]
      1  2   [-111.86145708699996, 40.4945008710162]
      
      # add new column
      df['USNG'] = ['Note 1', 'Note 2']
      
      # display(df)
         i                                       loc    USNG
      0  1  [-112.01268501699997, 40.64249414272372]  Note 1
      1  2   [-111.86145708699996, 40.4945008710162]  Note 2
      
      # write the dataframe to recarray
      data = df.to_records(index=False)
      
      print(data)
      [out]:
      rec.array([(1, list([-112.01268501699997, 40.64249414272372]), 'Note 1'),
                 (2, list([-111.86145708699996, 40.4945008710162]), 'Note 2')],
                dtype=[('i', '<i8'), ('loc', 'O'), ('USNG', 'O')])
      

      【讨论】:

        【解决方案5】:

        Tonsic 提到了import numpy.lib.recfunctions as rfn 的recfunctions。在这种情况下,适合您的更简单的 recfunction 函数是 rfn.merge_arrays() (docs)。

        【讨论】:

          【解决方案6】:

          使用 200 万多个数组,我立即注意到 Warren Weckesser 的 solution 和 Tonsic 的 ones 之间的巨大差异(非常感谢你们俩)

          与

          first_array
          [out]
          array([(1633046400299000, 1.34707, 1.34748),
                 (1633046400309000, 1.347  , 1.34748),
                 (1633046400923000, 1.347  , 1.34749), ...,
                 (1635551693846000, 1.36931, 1.36958),
                 (1635551693954000, 1.36925, 1.36952),
                 (1635551697902000, 1.3692 , 1.36947)],
                dtype=[('timestamp', '<i8'), ('bid', '<f8'), ('ask', '<f8')])
          

          和

          second_array
          [out]
          array([('2021-10-01T00:00:00.299000',), ('2021-10-01T00:00:00.309000',),
                 ('2021-10-01T00:00:00.923000',), ...,
                 ('2021-10-29T23:54:53.846000',), ('2021-10-29T23:54:53.954000',),
                 ('2021-10-29T23:54:57.902000',)], dtype=[('date_time', '<M8[us]')])
          

          我明白了

          %timeit rfn.merge_arrays((first_array, second_array), flatten=True)
          [out]
          13.8 s ± 1.11 s per loop (mean ± std. dev. of 7 runs, 1 loop each)
          

          和

          %timeit rfn.append_fields(first_array, 'date_time', second_array, dtypes='M8[us]').data
          [out]
          2.12 s ± 146 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
          

          好多了(注意最后的.data,以避免得到mask和fill_value)

          而使用类似的东西

          def building_new(first_array, other_array):
              new_array = np.zeros(
                  first_array.size, 
                  dtype=[('timestamp', '<i8'), ('bid', '<f8'), ('ask', '<f8'), ('date_time', '<M8[us]')])
              new_array[['timestamp', 'bid', 'ask']] = first_array[['timestamp', 'bid', 'ask']]
              new_array['date_time'] = other_array
              return new_array
          

          (请注意,在结构化数组中,每一行都是一个元组,因此 size 效果很好)

          我明白了

          %timeit building_new(first_array, second_array)
          [out]
          67.2 ms ± 3.56 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
          

          三者的输出是一样的

          [out]
          array([(1633046400299000, 1.34707, 1.34748, '2021-10-01T00:00:00.299000'),
                 (1633046400309000, 1.347  , 1.34748, '2021-10-01T00:00:00.309000'),
                 (1633046400923000, 1.347  , 1.34749, '2021-10-01T00:00:00.923000'),
                 ...,
                 (1635551693846000, 1.36931, 1.36958, '2021-10-29T23:54:53.846000'),
                 (1635551693954000, 1.36925, 1.36952, '2021-10-29T23:54:53.954000'),
                 (1635551697902000, 1.3692 , 1.36947, '2021-10-29T23:54:57.902000')],
                dtype=[('timestamp', '<i8'), ('bid', '<f8'), ('ask', '<f8'), ('date_time', '<M8[us]')])
          

          最后的想法:

          创建新数组而不是recfunctions,第二个数组甚至不需要是结构化的

          third_array
          [out]
          array(['2021-10-01T00:00:00.299000', '2021-10-01T00:00:00.309000',
                 '2021-10-01T00:00:00.923000', ..., '2021-10-29T23:54:53.846000',
                 '2021-10-29T23:54:53.954000', '2021-10-29T23:54:57.902000'],
                dtype='datetime64[us]')
          
          %timeit building_new(first_array, third_array)
          [out]
          67 ms ± 1.58 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-07-16
            • 1970-01-01
            相关资源
            最近更新 更多