【问题标题】:Python pandas: how to obtain the datatypes of objects in a mixed-datatype column?Python pandas:如何获取混合数据类型列中对象的数据类型?
【发布时间】:2021-01-19 13:28:16
【问题描述】:

给定一个pandas.DataFrame,其中有一列包含混合数据类型,例如

df = pd.DataFrame({'mixed': [pd.Timestamp('2020-10-04'), 999, 'a string']})

我想知道如何获取列(系列)中各个对象的数据类型?假设我想修改 Series 中所有特定类型的条目,例如将所有整数乘以某个因子。

我可以迭代推导出一个掩码并在loc 中使用它,比如

m = np.array([isinstance(v, int) for v in df['mixed']])

df.loc[m, 'mixed'] *= 10

# df
#                  mixed
# 0  2020-10-04 00:00:00
# 1                 9990
# 2             a string

这可以解决问题,但我想知道是否有更多 pandastic 的方式来做到这一点?

【问题讨论】:

  • 我会反过来想:如果发生“真正的”高效矢量化(内置 C++ 级优化函数),这些函数是否可能自己执行昂贵的类型检查为每个元素?我想这不太可能,因为它违背了有效矢量化的中心思想。因此,我想无法避免 python 级别的类型检查。在这种情况下,映射/应用/列表理解/等。是not likely to differ much efficiency-wise。 (P.s. 这个论点与句法美学无关。)

标签: python pandas types


【解决方案1】:

仍需致电type

m = df.mixed.map(lambda x : type(x).__name__)=='int'
df.loc[m, 'mixed']*=10
df
                 mixed
0  2020-10-04 00:00:00
1                 9990
2             a string

【讨论】:

    【解决方案2】:

    一个想法是通过to_numericerrors='coerce' 测试数字是否为非缺失值:

    m = pd.to_numeric(df['mixed'], errors='coerce').notna()
    df.loc[m, 'mixed'] *= 10
    print (df)
                     mixed
    0  2020-10-04 00:00:00
    1                 9990
    2             a string
    

    不幸的是速度很慢,还有一些想法:

    N = 1000000
    df = pd.DataFrame({'mixed': [pd.Timestamp('2020-10-04'), 999, 'a string'] * N})
    
    
    In [29]: %timeit df.mixed.map(lambda x : type(x).__name__)=='int'
    1.26 s ± 83.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    In [30]: %timeit np.array([isinstance(v, int) for v in df['mixed']])
    1.12 s ± 77.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    In [31]: %timeit pd.to_numeric(df['mixed'], errors='coerce').notna()
    3.07 s ± 55.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    

    In [34]: %timeit ([isinstance(v, int) for v in df['mixed']])
    909 ms ± 8.45 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    In [35]: %timeit df.mixed.map(lambda x : type(x))=='int'
    877 ms ± 8.69 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    In [36]: %timeit df.mixed.map(lambda x : type(x) =='int')
    842 ms ± 6.29 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    In [37]: %timeit df.mixed.map(lambda x : isinstance(x, int))
    807 ms ± 13.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    

    默认情况下,这里的 Pandas 不能有效地使用矢量化,因为混合值 - 因此是必要的元素方法。

    【讨论】:

      【解决方案3】:

      如果你想对所有“数字”进行复数,那么你可以使用以下。

      让我们使用pd.to_numeric 和参数errors = 'coerce'fillna

      df['mixed'] = (pd.to_numeric(df['mixed'], errors='coerce') * 10).fillna(df['mixed'])
      df
      

      输出:

                       mixed
      0  2020-10-04 00:00:00
      1                 9990
      2             a string
      

      让我们在列中添加一个浮点数

      df = pd.DataFrame({'mixed': [pd.Timestamp('2020-10-04'), 999, 'a string', 100.3]})
      

      使用@BenYo:

      m = df.mixed.map(lambda x : type(x).__name__)=='int'
      df.loc[m, 'mixed']*=10
      df
      

      输出(注意只有整数 999 乘以 10):

                       mixed
      0  2020-10-04 00:00:00
      1                 9990
      2             a string
      3                100.3
      

      使用@jezrael 和类似的解决方案:

      m = pd.to_numeric(df['mixed'], errors='coerce').notna()
      df.loc[m, 'mixed'] *= 10
      print(df)
      
      # Or this solution
      # df['mixed'] = (pd.to_numeric(df['mixed'], errors='coerce') * 10).fillna(df['mixed'])
      

      输出(注意所有数字都乘以 10):

                       mixed
      0  2020-10-04 00:00:00
      1                 9990
      2             a string
      3                 1003
      

      【讨论】:

      • 这很好,to_numeric 不允许区分 int 和 float。实际上,这可能没问题(如果它只是要修改所有数字)。
      【解决方案4】:

      如果计算量大,内存少,建议加一列表示混合的类型,效率更高。构建完这一列后,计算速度会快很多。

      代码如下:

      N = 1000000
      df = pd.DataFrame({'mixed': [pd.Timestamp('2020-10-04'), 999, 'a string'] * N})
      df["mixed_type"] = df.mixed.map(lambda x: type(x).__name__).astype('category')
      m = df.mixed_type == 'int'
      df.loc[m, "mixed"] *= 10
      del df["mixed_type"] # after you finish all your calculation
      
      

      mixed_type 列 repr 是

      0          Timestamp
      1                int
      2                str
      3          Timestamp
      4                int
                   ...    
      2999995          int
      2999996          str
      2999997    Timestamp
      2999998          int
      2999999          str
      Name: mixed, Length: 3000000, dtype: category
      Categories (3, object): [Timestamp, int, str]
      

      时间到了

      >>> %timeit df.mixed_type == 'int'
      472 µs ± 57.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
      
      >>> %timeit df.mixed.map(lambda x : type(x).__name__)=='int'
      1.12 s ± 87.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
      

      【讨论】:

      • 这是我最初的想法,只是我希望有一种方法可以为这些类型创建一个额外的列 :) 如果应该为多种不同类型进行操作,无论如何都是一个好方法!
      • @MrFuppes 我猜 BEN_YO 的答案是唯一的答案,你必须调用 type 来获取数据类型,pandas 只为混合 dtype pbpython.com/pandas_dtypes.html 维护一个系列级别的 dtype object。其他答案仅适用于 int 类型。我的回答是显示category dtype 帮助使其更快。
      • @MrFuppes 我认为维护每个单元格的数据类型远非效率,大多数人不会使用它们。如果有人有这个要求,就自己做吧。
      • 对我来说,Bill Huang 对这个问题的评论是一个很好的总结。其他一切或多或少都是句法美学。但是我希望这里的所有答案都希望在遇到问题时对其他人有所帮助。
      【解决方案5】:

      对于不是很长的数据帧,我也可以建议这种方式:

      df = df.assign(mixed = lambda x: x.apply(lambda s: s['mixed']*10 if isinstance(s['mixed'], int) else s['mixed'],axis=1))
      

      【讨论】:

        猜你喜欢
        • 2018-05-12
        • 2019-10-11
        • 1970-01-01
        • 2018-05-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-11-05
        • 2021-01-31
        相关资源
        最近更新 更多