带有缺失数据的 pandas groupby 操作

5
在pandas数据框中,我有一个类似于以下格式的列:
0         M
1         E
2         L
3       M.1
4       M.2
5       M.3
6       E.1
7       E.2
8       E.3
9       E.4
10      L.1
11      L.2
12    M.1.a
13    M.1.b
14    M.1.c
15    M.2.a
16    M.3.a
17    E.1.a
18    E.1.b
19    E.1.c
20    E.2.a
21    E.3.a
22    E.3.b
23    E.4.a

我需要将首字母为 E、M 或 L 的所有值分组,然后针对每个组,我需要创建一个子组,其中索引为 1、2 或 3,其中将包含每个小写字母(a、b、c 等)的记录。可能的解决方案应适用于任何级别的连接元素(在本例中,级别数为3(例如:A.1.a))。

0    1    2
E    1    a
          b
          c
     2    a
     3    a
          b
     4    a
L    1
     2
M    1    a
          b
          c
     2    a
     3    a

我尝试了以下方法:
df.groupby([0,1,2]).count()

但结果缺少L级别,因为它没有最后一个子级别的记录。
解决方法是添加一个虚拟变量,然后再将其删除...就像这样:
df[2][(df[0]=='L') & (df[2].isnull()) & (df[1].notnull())]='x'
df = df.replace(np.nan,' ', regex=True)
df.sort_values(0, ascending=False, inplace=True)
newdf = df.groupby([0,1,2]).count()

它提供了:

0    1    2
E    1    a
          b
          c
     2    a
     3    a
          b
     4    a
L    1    x
     2    x
M    1    a
          b
          c
     2    a
     3    a

我随后在代码中处理dummy条目x...

如何避免使用groupby的这种笨拙方式?

2个回答

1

假设要处理的列由s表示,我们可以:

  1. 使用"."作为分隔符,并使用expand=True拆分以产生扩展的DF

  2. fnc:检查分组帧的所有元素是否都只包含None,然后用通过列表推导式建立的虚拟条目""替换它们。之后在过滤后的列表上调用系列构造函数。这里的任何None都将使用dropna进行删除。

  3. 根据01列名执行groupby并将2应用于fnc


split_str = s.str.split(".", expand=True)
fnc = lambda g: pd.Series(["" if all(x is None for x in g) else x for x in g]).dropna()
split_str.groupby([0, 1])[2].apply(fnc)

产生:

0  1   
E  1  1    a
      2    b
      3    c
   2  1    a
   3  1    a
      2    b
   4  1    a
L  1  0    
   2  0    
M  1  1    a
      2    b
      3    c
   2  1    a
   3  1    a
Name: 2, dtype: object

为了获得一个扁平化的DF,需要将索引重置为与分组前使用的级别相同:
split_str.groupby([0, 1])[2].apply(fnc).reset_index(level=[0, 1]).reset_index(drop=True)

产生:

    0  1  2
0   E  1  a
1   E  1  b
2   E  1  c
3   E  2  a
4   E  3  a
5   E  3  b
6   E  4  a
7   L  1   
8   L  2   
9   M  1  a
10  M  1  b
11  M  1  c
12  M  2  a
13  M  3  a

0

也许你需要用正则表达式找到一种方法。

import pandas as pd

df = pd.read_clipboard(header=None).iloc[:, 1]
df2 = df.str.extract(r'([A-Z])\.?([0-9]?)\.?([a-z]?)')
print df2.set_index([0,1])

结果是:

     2
0 1   
M     
E     
L     
M 1   
  2   
  3   
E 1   
  2   
  3   
  4   
L 1   
  2   
M 1  a
  1  b
  1  c
  2  a
  3  a
E 1  a
  1  b
  1  c
  2  a
  3  a
  3  b
  4  a

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接