Pandas分割列名

3

我有一个测试数据框长这样:

data = pd.DataFrame([[0,0,0,3,6,5,6,1],[1,1,1,3,4,5,2,0],[2,1,0,3,6,5,6,1],[3,0,0,2,9,4,2,1]], columns=["id", "sex", "split", "group0Low", "group0High", "group1Low", "group1High", "trim"])

grouped = data.groupby(['sex','split']).mean()

stacked = grouped.stack().reset_index(level=2)
stacked.columns = ['group_level', 'mean']

接下来,我想将group_level分离出来,并堆叠这两个新因素:

stacked['group'] = stacked.group_level.str[:6]
stacked['level'] = stacked.group_level.str[6:]

这一切都很好。我的问题是:

如果我的列名称(“group0Low”,“group0High”,“group1Low”,“group1High”)彼此有些共同点,则可以正常工作。

如果我的列名称更像是“routeLow”,“routeHigh”,“landmarkLow”,“landmarkHigh”怎么办?在这种情况下,我该如何使用str来分割group_level?

这个问题与此处发布的问题类似:Slice/split string Series at various positions

不同之处在于,我的所有列子名称都不同且没有共性(而在其他帖子中,每个名称中都有组或类)。是否有一个正则表达式字符串或其他方法可以用来进行堆叠?

2个回答

3

这里有另一种方法。它假设低/高组以单词LowHigh结尾,因此我们可以使用.str.endswith()来识别哪些行是低/高。

这是示例数据

df = pd.DataFrame('group0Low group0High group1Low group1High routeLow routeHigh landmarkLow landmarkHigh'.split(), columns=['group_level'])
df

    group_level
0     group0Low
1    group0High
2     group1Low
3    group1High
4      routeLow
5     routeHigh
6   landmarkLow
7  landmarkHigh

使用np.where,我们可以做以下操作

df['level'] = np.where(df['group_level'].str.endswith('Low'), 'Low', 'High')
df['group'] = np.where(df['group_level'].str.endswith('Low'), df['group_level'].str[:-3], df['group_level'].str[:-4])

df

    group_level level     group
0     group0Low   Low    group0
1    group0High  High    group0
2     group1Low   Low    group1
3    group1High  High    group1
4      routeLow   Low     route
5     routeHigh  High     route
6   landmarkLow   Low  landmark
7  landmarkHigh  High  landmark

谢谢您的建议。我通过使用str.contains()进行了改进,现在一切都很顺利! - Simon

2

我想这取决于你所处理的字符串的普遍性。假设所有级别都由大写字母分隔,你可以这样做:

In [30]:    
s = pd.Series(['routeHigh', 'routeLow', 'landmarkHigh', 
               'landmarkLow', 'routeMid', 'group0Level'])
s.str.extract('([\d\w]*)([A-Z][\w\d]*)')

Out[30]:
    0       1
0   route   High
1   route   Low
2   landmark    High
3   landmark    Low
4   route   Mid
5   group0  Level

您甚至可以在同一行中命名结果的列,方法如下:

s.str.extract('(?P<group>[\d\w]*)(?P<Level>[A-Z][\w\d]*)')

因此,在您的使用情况下,您可以这样做:
group_level_df = stacked.group_level.extract('(?P<group>[\d\w]*)(?P<Level>[A-Z][\w\d]*)')
stacked = pd.concat([stacked, group_level_df])

以下是另一种方法,假设您事先只知道级别名称。 假设您有三个级别:

lower = stacked.group_level.str.lower()
for level in ['low', 'mid', 'high']:

    rows_in = lower.str.contains(level)
    stacked.loc[rows_in, 'level'] = level.capitalize()  
    stacked.loc[rows_in, 'group'] = stacked.group_level[rows_in].str.replace(level, '')

只要级别名称不出现在组名中,例如“highballHigh”,这个应该是有效的。如果group_level中没有包含这些级别,则相应行的值将为null。

我看到了你之前编辑的答案,它实际上完美地解决了我的问题,但我也看到了使用str.contains()存在的限制。我对你使用str.extract方法并在同一时间命名列非常感兴趣。然而,似乎它只适用于系列,而不适用于数据帧(错误:“DataFrame”对象没有“str”属性)。有其他的替代方案吗? - Simon
1
不,你只需要执行 stacked.group_level.str.extract,也就是在相应的列上调用它。正如你所说,它是一个系列方法。我使用了自己的系列来展示更多的例子,这就是为什么我没有放进去 stacked.group_level。我删除了之前的答案,因为 strip 方法一点都不好用,但后来我想到了使用 replace,这就是上面第二种方法中的方法。如果你采用这种方式,请勿使用 strip,而是使用 replace。 - JoeCondron
如果我这样做:stacked['condition'] = stacked.condition.str.extract('(?P<map>[\d\w])(?P<load>[A-Z][\w\d])'),它只会在列中存储路线/地标,而忽略低/高因素。似乎它试图将列名称中的两个内容分开,并将其存储到名为condition的单个列中,但我实际上需要创建两个新列,一个用于路线/地标,另一个用于低/高。 - Simon
所以看起来我需要一个语句用于 stacked['group'] = ... 和一个语句用于 stacked['level'] = ...,除非有一种方法可以从单个提取/正则表达式语句中创建两个新列? - Simon
提取方法将创建一个数据框,其中包含您传递的模式中指定的组数,本例中为两个。组在模式中由括号分隔。我已编辑问题以显示如何在您的情况下应用它。 - JoeCondron

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