代码之家  ›  专栏  ›  技术社区  ›  cs95 abhishek58g

是否有一种有效的方法来检查列是否有混合的数据类型?

  •  18
  • cs95 abhishek58g  · 技术社区  · 7 年前

    np.random.seed(0)
    s1 = pd.Series([1, 2, 'a', 'b', [1, 2, 3]])
    s2 = np.random.randn(len(s1))
    s3 = np.random.choice(list('abcd'), len(s1))
    
    
    df = pd.DataFrame({'A': s1, 'B': s2, 'C': s3})
    df
               A         B  C
    0          1  1.764052  a
    1          2  0.400157  d
    2          a  0.978738  c
    3          b  2.240893  a
    4  [1, 2, 3]  1.867558  a
    

    列“A”有混合的数据类型。我想提出一个非常快速的方法来确定这一点。这并不像检查 type == object

    我可以考虑和你一起做这件事

    df.applymap(type).nunique() > 1
    
    A     True
    B    False
    C    False
    dtype: bool
    

    type applymap 速度相当慢。特别是对于较大的框架。

    %timeit df.applymap(type).nunique() > 1
    3.95 ms ± 88 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    

    我们能做得更好吗(也许用NumPy)?如果你的论点足够有说服力,我可以接受“不”。:-)

    3 回复  |  直到 7 年前
        1
  •  5
  •   Paul Panzer    7 年前

    这里有一种方法,它利用了Python3中无法比较不同类型的事实。我们的想法是跑步 max 在阵列上,作为一个内置的应该是相当快的。而且它会缩短电路。

    def ismixed(a):
        try:
            max(a)
            return False
        except TypeError as e: # we take this to imply mixed type
            msg, fst, and_, snd = str(e).rsplit(' ', 3)
            assert msg=="'>' not supported between instances of"
            assert and_=="and"
            assert fst!=snd
            return True
        except ValueError as e: # catch empty arrays
            assert str(e)=="max() arg is an empty sequence"
            return False
    

    不过,它不能捕获混合的数字类型。此外,不支持比较的对象可能会导致这种情况。

    但它相当快。如果我们剥去所有的 pandas 间接费用:

    v = df.values
    
    list(map(is_mixed, v.T))
    # [True, False, False]
    timeit(lambda: list(map(ismixed, v.T)), number=1000)
    # 0.008936170022934675
    

    作比较

    timeit(lambda: list(map(infer_dtype, v.T)), number=1000)
    # 0.02499613002873957
    
        2
  •  15
  •   Alex Riley    7 年前

    在熊猫身上有 infer_dtype()

    用赛昂语写的( code link ),它返回一个字符串,总结传递的对象中的值。它在熊猫的内部使用了很多,所以我们可以合理地预期,它的设计考虑到了效率。

    >>> from pandas.api.types import infer_dtype
    

    现在,列A是整数和一些其他类型的混合:

    >>> infer_dtype(df.A)
    'mixed-integer'
    

    >>> infer_dtype(df.B)
    'floating'
    

    >>> infer_dtype(df.B)
    'string'
    

    混合值的一般“catchall”类型只是“mixed”:

    >>> infer_dtype(['a string', pd.Timedelta(10)])
    'mixed'
    

    浮点数和整数的混合是“混合整数浮点数”:

    >>> infer_dtype([3.141, 99])
    'mixed-integer-float'
    

    为了实现您在问题中描述的功能,一种方法可以是创建一个能够捕获相关混合情况的功能:

    def is_mixed(col):
        return infer_dtype(col) in ['mixed', 'mixed-integer']
    

    那么你有:

    >>> df.apply(is_mixed)
    A     True
    B    False
    C    False
    dtype: bool
    
        3
  •  3
  •   Ben.T    7 年前

    不确定你需要怎样的结果,但你可以 map 这个 type df.values.ravel() len 属于 set 优于1,适用于每片 l 例如:

    l = list(map(type, df.values.ravel()))
    print ({df.columns[i]:len(set(l[i::df.shape[1]])) > 1 for i in range(df.shape[1])})
    {'A': True, 'B': False, 'C': False}
    

    时间:

    %timeit df.applymap(type).nunique() > 1
    #3.25 ms ± 516 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    
    %%timeit 
    l = list(map(type, df.values.ravel()))
    {df.columns[i]:len(set(l[i::df.shape[1]])) > 1 for i in range(df.shape[1])}
    #100 µs ± 5.08 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    

    对于较大的数据帧进行编辑,时间上的改进不那么有趣:

    dfl = pd.concat([df]*100000,ignore_index=True)
    
    %timeit dfl.applymap(type).nunique() > 1
    #519 ms ± 61.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    %%timeit
    l = list(map(type, dfl.values.ravel()))
    {dfl.columns[i]:len(set(l[i::dfl.shape[1]])) > 1 for i in range(dfl.shape[1])}
    #254 ms ± 33.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    

    基于相同想法的更快的解决方案:

    %timeit { col: len(set(map(type, dfl[col])))>1 for col in dfl.columns}
    #124 ms ± 15.2 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)