考虑
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
那样简单,因为那样会将“ C”标识为假阳性。
我可以考虑使用
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)吗?如果您的论点足够令人信服,我可以接受“否”。 :-)
答案 0 :(得分:13)
大熊猫中有infer_dtype()
,在这里可能会有所帮助。
用Cython(code link)编写,它返回一个字符串,该字符串汇总了所传递对象中的值。它在熊猫内部使用了很多东西,因此我们可以合理地期望它的设计考虑到了效率。
>>> from pandas.api.types import infer_dtype
现在,A列是整数和一些其他类型的混合:
>>> infer_dtype(df.A)
'mixed-integer'
B列的值均为浮点型:
>>> infer_dtype(df.B)
'floating'
列C包含字符串:
>>> infer_dtype(df.B)
'string'
用于混合值的常规“ catchall”类型就是“ mixed”:
>>> infer_dtype(['a string', pd.Timedelta(10)])
'mixed'
浮点数和整数的混合是“ mixed-integer-float”:
>>> 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
答案 1 :(得分:5)
这是一种使用以下事实的方法:在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 :(得分:3)
不确定如何获得结果,但是可以将map
type
到df.values.ravel()
并创建一个列名称的字典,该字典的链接指向{{1 }}中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)