web-dev-qa-db-ja.com

列に混合dtypeがあるかどうかをチェックする効率的な方法はありますか?

検討する

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」には混合データ型があります。私はこれを決定するための本当に迅速な方法を考え出したいと思います。 「C」が誤検知として識別されるため、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で)もっとうまくやれるでしょうか?あなたの議論が十分に説得力があるならば、私は「いいえ」を受け入れることができます。 :-)

18
cs95

これは、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
5
Paul Panzer

pandasには infer_dtype() があります。これはここで役立つかもしれません。

Cython( コードリンク )で記述され、渡されたオブジェクトの値を要約した文字列を返します。パンダの内部で多く使用されているので、効率を念頭に置いて設計されていると合理的に予想できます。

>>> 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'

混合値の一般的な「キャッチオール」タイプは、単に「混合」です。

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

Floatと整数の組み合わせは「mixed-integer-float」です。

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

質問で説明する関数を作成するための1つのアプローチは、関連する混合ケースをキャッチする関数を作成することです。

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

次に、次のようになります。

>>> df.apply(is_mixed)
A     True
B    False
C    False
dtype: bool
14
Alex Riley