如何有效地重新编码和计数

时间:2016-09-09 19:55:21

标签: python pandas

我有一个大的csv,每行有三个字符串:

a,c,d
c,a,e
f,g,f
a,c,b
c,a,d
b,f,s
c,a,c

我在前两列中读取了将字符串重新编码为整数,然后删除重复项,计算每行的副本数量如下:

import pandas as pd
df = pd.read_csv("test.csv", usecols=[0,1], prefix="ID_", header=None)
letters = set(df.values.flat)
df.replace(to_replace=letters, value=range(len(letters)), inplace=True)
df1 = df.groupby(['ID_0', 'ID_1']).size().rename('count').reset_index()
print df1

这给出了:

   ID_0  ID_1  count
0     0     1      2
1     1     0      3
2     2     4      1
3     4     3      1

这正是我所需要的。

然而,由于我的数据很大,我想做两处改进。

  • 我如何进行groupby然后重新编码而不是反过来?问题是我无法做df1[['ID_0','ID_0']].replace(to_replace=letters, value=range(len(letters)), inplace = True)。这给出了错误
      

    “正在尝试在DataFrame的切片副本上设置值”

  • 如何避免创建df1?这就是整个事情。

2 个答案:

答案 0 :(得分:3)

我喜欢使用sklearn.preprocessing.LabelEncoder来进行数字转换:

from sklearn.preprocessing import LabelEncoder

# Perform the groupby (before converting letters to digits).
df = df.groupby(['ID_0', 'ID_1']).size().rename('count').reset_index()

# Initialize the LabelEncoder.
le = LabelEncoder()
le.fit(df[['ID_0', 'ID_1']].values.flat)

# Convert to digits.
df[['ID_0', 'ID_1']] = df[['ID_0', 'ID_1']].apply(le.transform)

结果输出:

   ID_0  ID_1  count
0     0     2      2
1     1     3      1
2     2     0      3
3     3     4      1

如果您想稍后转换回字母,可以使用le.inverse_transform

df[['ID_0', 'ID_1']] = df[['ID_0', 'ID_1']].apply(le.inverse_transform)

按预期回映:

  ID_0 ID_1  count
0    a    c      2
1    b    f      1
2    c    a      3
3    f    g      1

如果您只想知道哪个数字对应哪个字母,您可以查看le.classes_属性。这将为您提供一个字母数组,由其编码的数字索引:

le.classes_ 

['a' 'b' 'c' 'f' 'g']

要获得更直观的表现形式,您可以投射为系列:

pd.Series(le.classes_)

0    a
1    b
2    c
3    f
4    g

<强>计时

使用更大版本的示例数据并进行以下设置:

df2 = pd.concat([df]*10**5, ignore_index=True)

def root(df):
    df = df.groupby(['ID_0', 'ID_1']).size().rename('count').reset_index()
    le = LabelEncoder()
    le.fit(df[['ID_0', 'ID_1']].values.flat)
    df[['ID_0', 'ID_1']] = df[['ID_0', 'ID_1']].apply(le.transform)
    return df

def pir2(df):
    unq = np.unique(df)
    mapping = pd.Series(np.arange(unq.size), unq)
    return df.stack().map(mapping).unstack() \
      .groupby(df.columns.tolist()).size().reset_index(name='count')

我得到以下时间:

%timeit root(df2)
10 loops, best of 3: 101 ms per loop

%timeit pir2(df2)
1 loops, best of 3: 1.69 s per loop

答案 1 :(得分:2)

新答案

unq = np.unique(df)
mapping = pd.Series(np.arange(unq.size), unq)

df.stack().map(mapping).unstack() \
  .groupby(df.columns.tolist()).size().reset_index(name='count')

enter image description here

旧答案

df.stack().rank(method='dense').astype(int).unstack() \
  .groupby(df.columns.tolist()).size().reset_index(name='count')

enter image description here