【问题标题】:Create Empty Dataframe in Pandas specifying column types在 Pandas 中创建指定列类型的空数据框
【发布时间】:2022-03-22 17:46:02
【问题描述】:

我正在尝试创建一个带有索引的空数据框并指定列类型。我这样做的方式如下:

df = pd.DataFrame(index=['pbp'],
                  columns=['contract',
                           'state_and_county_code',
                           'state',
                           'county',
                           'starting_membership',
                           'starting_raw_raf',
                           'enrollment_trend',
                           'projected_membership',
                           'projected_raf'],
                  dtype=['str', 'str', 'str', 'str',
                         'int', 'float', 'float',
                         'int', 'float'])

但是,我收到以下错误,

TypeError: data type not understood

这是什么意思?

【问题讨论】:

  • 我认为您不能以这种方式指定 dtypes,您可以传递单个类型,例如 str,但不是字符串列表。当您分配列值时,将推断出 dtype。我认为根本不需要指定
  • @EdChum 根据docs,这是真的,我想知道为什么构造函数不允许这样做......用用于分配目的的类型?

标签: python pandas


【解决方案1】:

您可以使用以下内容:

df = pd.DataFrame({'a': pd.Series(dtype='int'),
                   'b': pd.Series(dtype='str'),
                   'c': pd.Series(dtype='float')})

或更抽象地说:

df = pd.DataFrame({c: pd.Series(dtype=t) for c, t in {'a': 'int', 'b': 'str', 'c': 'float'}.items()})

那么如果你调用 df 你有:

>>> df 
Empty DataFrame 
Columns: [a, b, c]
Index: []

如果你检查它的类型:

>>> df.dtypes
a      int32
b     object
c    float64
dtype: object

【讨论】:

  • 这个答案也适用于非空数据帧,这是我正在寻找的:df = pd.DataFrame({'x': [1, 2, 4], 'y': pd.Series(['odd', 'even', 'even'], dtype='category')})
【解决方案2】:

一种方法:

import numpy
import pandas

dtypes = numpy.dtype(
    [
        ("a", str),
        ("b", int),
        ("c", float),
        ("d", numpy.datetime64),
    ]
)
df = pandas.DataFrame(numpy.empty(0, dtype=dtypes))

【讨论】:

    【解决方案3】:

    这是一个老问题,但我没有看到可靠的答案(尽管@eric_g 非常接近)。

    您只需要创建一个带有键值对字典的空数据框。键是您的列名,值是空数据类型。

    因此,在您的示例数据集中,它将如下所示(pandas 0.25 和 python 3.7):

    variables = {'contract':'',
                 'state_and_county_code':'',
                 'state':'',
                 'county':'',
                 'starting_membership':int(),
                 'starting_raw_raf':float(),
                 'enrollment_trend':float(),
                 'projected_membership':int(),
                 'projected_raf':float()}
    
    df = pd.DataFrame(variables, index=[])
    

    在旧版 pandas 中,可能需要这样做:

    df = pd.DataFrame(columns=[variables])
    

    【讨论】:

    • 我认为这不起作用,因为 Pandas 会抛出一个错误,指出 dict is unhashable type(这是可以理解的)。而且,文档中没有提到这种格式。
    • 我正在我的代码中积极使用它,它工作得很好。我用的是 pandas 0.22.0,你呢?
    • 我也遇到了与@AnatolyScherbakov 相同的问题。我正在使用 0.23.0 。如果可行,这似乎是最直接的方法。
    • 我已经更新了上面的代码以使用最新版本的 python 和 pandas。希望对您有所帮助。
    【解决方案4】:

    这真的闻起来像个虫子。

    这是另一个(更简单的)解决方案。

    import pandas as pd
    import numpy as np
    
    def df_empty(columns, dtypes, index=None):
        assert len(columns)==len(dtypes)
        df = pd.DataFrame(index=index)
        for c,d in zip(columns, dtypes):
            df[c] = pd.Series(dtype=d)
        return df
    
    df = df_empty(['a', 'b'], dtypes=[np.int64, np.int64])
    print(list(df.dtypes)) # int64, int64
    

    【讨论】:

      【解决方案5】:

      只是一个评论。

      您可以使用np.dtype 解决类型错误:

      pd.DataFrame(index = ['pbp'], columns = ['a','b'], dtype = np.dtype([('str','float')]))
      

      但你得到的是:

      NotImplementedError: compound dtypes are not implementedin the DataFrame constructor
      

      【讨论】:

      • 这确实是正确的答案。即使修复了 TypeError,它仍然不是 pandas 费心去实现的东西。你甚至不能从现有的复合数据帧复制数据类型来启动一个新的空数据帧,这看起来确实是一个有效的用例。
      • @MikeJarvis 如果您想复制现有框架的 dtypes,您可以从该框架中选择 0 行并让您的空 DF 具有相同的 dtypes。例如cpy = df.loc[[False]*len(df)] 应该可以解决问题
      • 如果它不起作用,我不知道作为“正确答案”意味着什么。我想你是在说:“我希望这行得通”。
      • 这是一个误导性的“答案”,尽管它包含重要信息。也许它应该改写为:“即使你可以通过...绕过类型错误。它仍然不可能,因为 pandas 没有实现它:...”
      • @Jan 你是对的,这不是一个真正的答案。请随时更新/改写。
      【解决方案6】:

      我的解决方案(不设置索引)是使用列名初始化数据框并使用astype() 方法指定数据类型。

      df = pd.DataFrame(columns=['contract',
                           'state_and_county_code',
                           'state',
                           'county',
                           'starting_membership',
                           'starting_raw_raf',
                           'enrollment_trend',
                           'projected_membership',
                           'projected_raf'])
      df = df.astype( dtype={'contract' : str, 
                       'state_and_county_code': str,
                       'state': str,
                       'county': str,
                       'starting_membership': int,
                       'starting_raw_raf': float,
                       'enrollment_trend': float,
                       'projected_membership': int,
                       'projected_raf': float})
      

      【讨论】:

      • 我找到了同样的解决方案。您可以使用字典为您的数据框定义架构:schema = { 'name': str, 'number': float, 'date': np.datetime64} df = pd.DataFrame(columns=schema.keys()).astype(schema)
      • @SimonEjsing yours 是一个更优雅的解决方案,感谢分享
      • 清洁解决方案,也适用于非空数据帧。干得好!
      【解决方案7】:

      我在遇到同样的问题后发现了这个问题。我更喜欢以下解决方案 (Python 3) 来创建一个 no index 的空 DataFrame。

      import numpy as np
      import pandas as pd
      
      def make_empty_typed_df(dtype):
          tdict = np.typeDict
          types = tuple(tdict.get(t, t) for (_, t, *__) in dtype)
          if any(t == np.void for t in types):
              raise NotImplementedError('Not Implemented for columns of type "void"')
          return pd.DataFrame.from_records(np.array([tuple(t() for t in types)], dtype=dtype)).iloc[:0, :]
      

      测试一下...

      from itertools import chain
      
      dtype = [('col%d' % i, t) for i, t in enumerate(chain(np.typeDict, set(np.typeDict.values())))]
      dtype = [(c, t) for (c, t) in dtype if (np.typeDict.get(t, t) != np.void) and not isinstance(t, int)]
      
      print(make_empty_typed_df(dtype))
      

      输出:

      Empty DataFrame
      
      Columns: [col0, col6, col16, col23, col24, col25, col26, col27, col29, col30, col31, col32, col33, col34, col35, col36, col37, col38, col39, col40, col41, col42, col43, col44, col45, col46, col47, col48, col49, col50, col51, col52, col53, col54, col55, col56, col57, col58, col60, col61, col62, col63, col64, col65, col66, col67, col68, col69, col70, col71, col72, col73, col74, col75, col76, col77, col78, col79, col80, col81, col82, col83, col84, col85, col86, col87, col88, col89, col90, col91, col92, col93, col95, col96, col97, col98, col99, col100, col101, col102, col103, col104, col105, col106, col107, col108, col109, col110, col111, col112, col113, col114, col115, col117, col119, col120, col121, col122, col123, col124, ...]
      Index: []
      
      [0 rows x 146 columns]
      

      还有数据类型...

      print(make_empty_typed_df(dtype).dtypes)
      

      输出:

      col0      timedelta64[ns]
      col6               uint16
      col16              uint64
      col23                int8
      col24     timedelta64[ns]
      col25                bool
      col26           complex64
      col27               int64
      col29             float64
      col30                int8
      col31             float16
      col32              uint64
      col33               uint8
      col34              object
      col35          complex128
      col36               int64
      col37               int16
      col38               int32
      col39               int32
      col40             float16
      col41              object
      col42              uint64
      col43              object
      col44               int16
      col45              object
      col46               int64
      col47               int16
      col48              uint32
      col49              object
      col50              uint64
                     ...       
      col144              int32
      col145               bool
      col146            float64
      col147     datetime64[ns]
      col148             object
      col149             object
      col150         complex128
      col151    timedelta64[ns]
      col152              int32
      col153              uint8
      col154            float64
      col156              int64
      col157             uint32
      col158             object
      col159               int8
      col160              int32
      col161             uint64
      col162              int16
      col163             uint32
      col164             object
      col165     datetime64[ns]
      col166            float32
      col167               bool
      col168            float64
      col169         complex128
      col170            float16
      col171             object
      col172             uint16
      col173          complex64
      col174         complex128
      dtype: object
      

      添加索引变得很棘手,因为大多数数据类型都没有真正的缺失值,因此它们最终会被转换为具有本机缺失值的其他类型(例如,ints 被转换为 floats或objects),但如果您拥有指定类型的完整数据,那么您始终可以根据需要插入行,并且您的类型将受到尊重。这可以通过以下方式完成:

      df.loc[index, :] = new_row
      

      同样,正如@Hun 指出的那样,这不是 Pandas 的预期用途。

      【讨论】:

        【解决方案8】:

        从您的示例中获取列表列和 dtype,您可以执行以下操作:

        cdt={i[0]: i[1] for i in zip(columns, dtype)}    # make column type dict
        pdf=pd.DataFrame(columns=list(cdt))    # create empty dataframe
        pdf=pdf.astype(cdt)                    # set desired column types
        

        DataFrame 文档说在构造函数调用中只允许使用一个 dtype。

        【讨论】:

          【解决方案9】:

          我发现对我来说最简单的解决方法是简单地为每个单独的列连接一个空系列列表:

          import pandas as pd
          
          columns = ['contract',
                     'state_and_county_code',
                     'state',
                     'county',
                     'starting_membership',
                     'starting_raw_raf',
                     'enrollment_trend',
                     'projected_membership',
                     'projected_raf']
          dtype = ['str', 'str', 'str', 'str', 'int', 'float', 'float', 'int', 'float']
          df = pd.concat([pd.Series(name=col, dtype=dt) for col, dt in zip(columns, dtype)], axis=1)
          df.info()
          # <class 'pandas.core.frame.DataFrame'>
          # Index: 0 entries
          # Data columns (total 9 columns):
          # contract                 0 non-null object
          # state_and_county_code    0 non-null object
          # state                    0 non-null object
          # county                   0 non-null object
          # starting_membership      0 non-null int32
          # starting_raw_raf         0 non-null float64
          # enrollment_trend         0 non-null float64
          # projected_membership     0 non-null int32
          # projected_raf            0 non-null float64
          # dtypes: float64(3), int32(2), object(4)
          # memory usage: 0.0+ bytes
          

          【讨论】:

            【解决方案10】:

            pandas 不提供纯整数列。您可以使用浮点列并根据需要将该列转换为整数,也可以将其视为对象。您尝试实现的不是应该使用 pandas 的方式。但如果你真的很想这样做,你可以通过这样做来绕过 TypeError 消息。

            df1 =  pd.DataFrame(index=['pbp'], columns=['str1','str2','str2'], dtype=str)
            df2 =  pd.DataFrame(index=['pbp'], columns=['int1','int2'], dtype=int)
            df3 =  pd.DataFrame(index=['pbp'], columns=['flt1','flt2'], dtype=float)
            df = pd.concat([df1, df2, df3], axis=1)
            
                str1 str2 str2 int1 int2  flt1  flt2
            pbp  NaN  NaN  NaN  NaN  NaN   NaN   NaN
            

            您可以根据需要重新排列颜色顺序。但同样,这不是 pandas 应该使用的方式。

             df.dtypes
            str1     object
            str2     object
            str2     object
            int1     object
            int2     object
            flt1    float64
            flt2    float64
            dtype: object
            

            注意 int 被视为对象。

            【讨论】:

            • 你到底在说什么?当然 Pandas 支持整数列。
            • 不过,在没有数据的情况下传递 dtype=int 似乎确实存在问题。
            • 这绝对看起来像一个错误 - 仍然是最新版本中的行为。你提交了吗?
            • 它的预期行为,它在警告中列出。这是因为int 没有nan。您可以在docs 上阅读更多相关信息
            • @VictorUriarte 这并不能解释为什么不能在构造函数中指定 int 列。如果您要求一个 int 列,然后插入一个 nan,正确的行为是将该列提升为浮动,或引发异常
            【解决方案11】:

            您可以通过将字典传递给 DataFrame 构造函数来做到这一点:

            df = pd.DataFrame(index=['pbp'],
                              data={'contract' : np.full(1, "", dtype=str),
                                    'starting_membership' : np.full(1, np.nan, dtype=float),
                                    'projected_membership' : np.full(1, np.nan, dtype=int)
                                   }
                             )
            

            这将正确为您提供如下所示的数据框:

                 contract  projected_membership   starting_membership
            pbp     ""             NaN           -9223372036854775808
            

            使用数据类型:

            contract                 object
            projected_membership    float64
            starting_membership       int64
            

            也就是说,有两点需要注意:

            1) str 实际上不是 DataFrame 列可以处理的类型;相反,它会退回到一般情况object。它仍然可以正常工作。

            2) 为什么在starting_membership 下没有看到NaN?好吧,NaN 只为浮点数定义;整数没有“无”值,因此它将np.NaN 转换为整数。如果您想要一个不同的默认值,您可以在np.full 调用中更改它。

            【讨论】:

            • 当您可以使用空数组时,无需在列中放置一堆虚拟数据。
            【解决方案12】:

            在 Pandas 中创建空数据框,指定列类型:

            import pandas as pd
            
            c1 = pd.Series(data=None, dtype='string', name='c1')
            c2 = pd.Series(data=None, dtype='bool', name='c2')
            c3 = pd.Series(data=None, dtype='float', name='c3')
            c4 = pd.Series(data=None, dtype='int', name='c4')
            
            df = pd.concat([c1, c2, c3, c4], axis=1)
            
            df.info('verbose')
            

            我们将列创建为 Series 并为它们提供正确的 dtype,然后我们将 Series 连接到 DataFrame 中,就是这样

            我们有带有 dtypes 的 DataFrame 构造函数!

            <class 'pandas.core.frame.DataFrame'>
            Index: 0 entries
            Data columns (total 4 columns):
             #   Column  Non-Null Count  Dtype  
            ---  ------  --------------  -----  
             0   c1      0 non-null      string 
             1   c2      0 non-null      bool   
             2   c3      0 non-null      float64
             3   c4      0 non-null      int32  
            dtypes: bool(1), float64(1), int32(1), string(1)
            memory usage: 0.0+ bytes
            

            【讨论】:

              【解决方案13】:

              我推荐这个:

              columns = ["a", "b"]
              types = ['float32', 'str']
              predefined_size = 10
              
              df = pd.DataFrame({c: pd.Series(index=range(predefined_size), dtype=t) 
                                 for c,t in zip(columns, types)})
              

              优势

              • 支持旧版 pandas(例如 0.19.2)
              • 可以同时初始化类型和大小

              【讨论】:

                猜你喜欢
                • 2023-02-05
                • 2017-01-07
                • 2013-07-16
                • 2016-08-13
                • 1970-01-01
                • 2018-05-25
                • 1970-01-01
                • 1970-01-01
                • 2020-06-20
                相关资源
                最近更新 更多