web-dev-qa-db-ja.com

パンダの2列で一意のレコードをカウントする方法は?

私はパンダにデータフレームがあります:

In [10]: df
Out[10]:
    col_a    col_b  col_c  col_d
0  France    Paris      3      4
1      UK    Londo      4      5
2      US  Chicago      5      6
3      UK  Bristol      3      3
4      US    Paris      8      9
5      US   London     44      4
6      US  Chicago     12      4

ユニークな都市を数える必要があります。一意の状態をカウントできます

In [11]: df['col_a'].nunique()
Out[11]: 3

ユニークな都市を数えることができます

In [12]: df['col_b'].nunique()
Out[12]: 5

しかし、それは間違っています。米国のパリとフランスのパリは異なる都市だからです。だから今、私は次のようにしています:

In [13]: df['col_a_b'] = df['col_a'] + ' - ' + df['col_b']

In [14]: df
Out[14]:
    col_a    col_b  col_c  col_d         col_a_b
0  France    Paris      3      4  France - Paris
1      UK    Londo      4      5      UK - Londo
2      US  Chicago      5      6    US - Chicago
3      UK  Bristol      3      3    UK - Bristol
4      US    Paris      8      9      US - Paris
5      US   London     44      4     US - London
6      US  Chicago     12      4    US - Chicago

In [15]: df['col_a_b'].nunique()
Out[15]: 6

おそらくもっと良い方法がありますか?追加の列を作成せずに。

9
GhostKU

ngroupsを使用して

df.groupby(['col_a', 'col_b']).ngroups
Out[101]: 6

またはsetを使用して

len(set(Zip(df['col_a'],df['col_b'])))
Out[106]: 6
23
WeNYoBen

col_aおよびcol_bを選択し、重複をドロップしてから、結果データフレームのshape/lenを確認できます。

df[['col_a', 'col_b']].drop_duplicates().shape[0]
# 6

len(df[['col_a', 'col_b']].drop_duplicates())
# 6

groupbyNaNsを無視し、並べ替えプロセスを不必要に呼び出す可能性があるため、列にNaNsがある場合は、それに応じて使用する方法を選択します。

次のようなデータフレームを考えます。

df = pd.DataFrame({
    'col_a': [1,2,2,pd.np.nan,1,4],
    'col_b': [2,2,3,pd.np.nan,2,pd.np.nan]
})

print(df)

#   col_a  col_b
#0    1.0    2.0
#1    2.0    2.0
#2    2.0    3.0
#3    NaN    NaN
#4    1.0    2.0
#5    4.0    NaN

タイミング

df = pd.concat([df] * 1000)

%timeit df.groupby(['col_a', 'col_b']).ngroups
# 1000 loops, best of 3: 625 µs per loop

%timeit len(df[['col_a', 'col_b']].drop_duplicates())
# 1000 loops, best of 3: 1.02 ms per loop

%timeit df[['col_a', 'col_b']].drop_duplicates().shape[0]
# 1000 loops, best of 3: 1.01 ms per loop    

%timeit len(set(Zip(df['col_a'],df['col_b'])))
# 10 loops, best of 3: 56 ms per loop

%timeit len(df.groupby(['col_a', 'col_b']))
# 1 loop, best of 3: 260 ms per loop

結果

df.groupby(['col_a', 'col_b']).ngroups
# 3

len(df[['col_a', 'col_b']].drop_duplicates())
# 5

df[['col_a', 'col_b']].drop_duplicates().shape[0]
# 5

len(set(Zip(df['col_a'],df['col_b'])))
# 2003

len(df.groupby(['col_a', 'col_b']))
# 2003

だから違い:

オプション1:

df.groupby(['col_a', 'col_b']).ngroups

高速であり、NaNsを含む行を除外します。

オプション2および3:

len(df[['col_a', 'col_b']].drop_duplicates())
df[['col_a', 'col_b']].drop_duplicates().shape[0]

かなり高速で、NaNsを一意の値と見なします。

オプション4および5:

len(set(Zip(df['col_a'],df['col_b']))) 
len(df.groupby(['col_a', 'col_b'])) 

遅く、numpy.nan == numpy.nanはFalseなので、異なる(nan、nan)行は異なると見なされます。

8
Psidom
In [105]: len(df.groupby(['col_a', 'col_b']))
Out[105]: 6
4
MaxU

これを試してください、私は基本的にdfの行の数から重複するグループの数を引いています。これは、dfのすべてのカテゴリをグループ化することを前提としています

df.shape[0] - df[['col_a','col_b']].duplicated().sum()

774 µs ± 603 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)

0
Anuj