Предположим, у нас есть следующие упрощенные данные:

df = pd.DataFrame({'A':list('abcd'),
                   'B':list('efgh'),
                   'Data_mean':[1,2,3,4],
                   'Data_std':[5,6,7,8],
                   'Data_corr':[9,10,11,12],
                   'Text_one':['foo', 'bar', 'foobar', 'barfoo'],
                   'Text_two':['bar', 'foo', 'barfoo', 'foobar'],
                   'Text_three':['bar', 'bar', 'barbar', 'foofoo']})

   A  B  Data_mean  Data_std  Data_corr Text_one Text_two Text_three
0  a  e          1         5          9      foo      bar        bar
1  b  f          2         6         10      bar      foo        bar
2  c  g          3         7         11   foobar   barfoo     barbar
3  d  h          4         8         12   barfoo   foobar     foofoo

Я хочу перечислить столбцы с одинаковым префиксом. В этом случае префиксы: Data, Text. Таким образом, ожидаемый результат будет:

   A  B  Data_mean1  Data_std2  Data_corr3 Text_one1 Text_two2 Text_three3
0  a  e           1          5           9       foo       bar         bar
1  b  f           2          6          10       bar       foo         bar
2  c  g           3          7          11    foobar    barfoo      barbar
3  d  h           4          8          12    barfoo    foobar      foofoo

Обратите внимание на перечисленные столбцы.


Попытка решения № 1 .

def enumerate_cols(dataframe, prefix):
    cols = []
    num = 1
    for col in dataframe.columns:
        if col.startswith(prefix):
            cols.append(col + str(num))
            num += 1
        else:
            cols.append(col)

    return cols
enumerate_cols(df, 'Data')

['A',
 'B',
 'Data_mean1',
 'Data_std2',
 'Data_corr3',
 'Text_one',
 'Text_two',
 'Text_three']

Попытка решения № 2 .

[c+str(x+1) for x, c in enumerate([col for col in df.columns if col.startswith('Data')])]
['Data_mean1', 'Data_std2', 'Data_corr3']

Вопрос : есть ли более простое решение для этого, я также посмотрел на df.filter(like='Data') и т. д. Но это также выглядело довольно надуманным.


Проблема XY
Просто убедитесь, что я не попал в проблему XY. Я хочу использовать pd.wide_to_long, но к столбцам stubnames необходимо добавить суффикс числа, чтобы можно было растопить кадр данных.

Как указано в документации:

С помощью заглушек [‘A ',‘ B'] эта функция ожидает найти одну или несколько групп столбцов с форматом A-суффикс1, A-суффикс2,…, B-суффикс1, B-суффикс2,

pd.wide_to_long(df, stubnames=['Data', 'Text'], i=['A', 'B'], j='grp', sep='_')

Это возвращает пустой фрейм данных.

8
Erfan 1 Июл 2019 в 20:16

4 ответа

Лучший ответ

Идея состоит в том, чтобы сгруппировать столбцы с одинаковым префиксом и установить для них счетчик.

Поскольку нам нужно обрабатывать столбец без префикса отдельно, нам нужно сделать это в два этапа, используя GroupBy.cumcount и np.where:

cols = df.columns.str.split('_').str[0].to_series()

df.columns = np.where(
    cols.groupby(level=0).transform('count') > 1, 
    cols.groupby(level=0).cumcount().add(1).astype(str).radd(df.columns), 
    cols
)
df
   A  B  Data_mean1  Data_std2  Data_corr3 Text_one1 Text_two2 Text_three3
0  a  e           1          5           9       foo       bar         bar
1  b  f           2          6          10       bar       foo         bar
2  c  g           3          7          11    foobar    barfoo      barbar
3  d  h           4          8          12    barfoo    foobar      foofoo

Более простым решением было бы установить столбцы, к которым вы не хотите добавлять суффикс в качестве индекса. Тогда вы можете просто сделать

df.set_index(['A', 'B'], inplace=True)
df.columns = (
    df.columns.str.split('_')
      .str[0]
      .to_series()
      .groupby(level=0)
      .cumcount()
      .add(1)
      .astype(str)
      .radd(df.columns))

df
     Data_mean1  Data_std2  Data_corr3 Text_one1 Text_two2 Text_three3
A B                                                                   
a e           1          5           9       foo       bar         bar
b f           2          6          10       bar       foo         bar
c g           3          7          11    foobar    barfoo      barbar
d h           4          8          12    barfoo    foobar      foofoo
3
cs95 1 Июл 2019 в 17:35

Согласно нашему разговору, метод melt

s=df.melt(['A','B']).assign(x=lambda x : x.groupby(x.variable.str.split('_').str[0]).cumcount(),y=lambda x : x.variable.str.split('_').str[0]) 

# after this the problem became a pivot problem 
pd.crosstab([s.A,s.B,s.x],columns=s.y,values=s.value,aggfunc='sum')
y      Data    Text
A B x              
a e 0     1     foo
    4     5     bar
    8     9     bar
b f 1     2     bar
    5     6     foo
    9    10     bar
c g 2     3  foobar
    6     7  barfoo
    10   11  barbar
d h 3     4  barfoo
    7     8  foobar
    11   12  foofoo
2
YO and BEN_W 1 Июл 2019 в 18:08

Вы также можете использовать defaultdict для создания счетчика для каждого префикса.

from collections import defaultdict

prefix_starting_location = 2
columns = df.columns[prefix_starting_location:]
prefixes = set(col.split('_')[0] for col in columns)

new_cols = []
dd = defaultdict(int)
for col in columns:
    prefix = col.split('_')[0]
    dd[prefix] += 1
    new_cols.append(col + str(dd[prefix]))
df.columns = df.columns[:prefix_starting_location].tolist() + new_cols
>>> df
   A  B  Data_mean1  Data_std2  Data_corr3 Text_one1 Text_two2 Text_three3
0  a  e           1          5           9       foo       bar         bar
1  b  f           2          6          10       bar       foo         bar
2  c  g           3          7          11    foobar    barfoo      barbar
3  d  h           4          8          12    barfoo    foobar      foofoo
​

Если префиксы известны:

prefixes = ['Data', 'Text']
new_cols = []
dd = defaultdict(int)
for col in df.columns:
    prefix = col.split('_')[0]
    if prefix in prefixes:
        dd[prefix] += 1
        new_cols.append(col + str(dd[prefix]))
    else:
        new_cols.append(col)

Если ваш символ разделения _ отсутствует ни в одном из ваших полей данных:

new_cols = []
dd = defaultdict(int)
for col in df.columns:
    if '_' in col:
        prefix = col.split('_')[0]
        dd[prefix] += 1
        new_cols.append(col + str(dd[prefix]))
    else:
        new_cols.append(col)

df.columns = new_cols
2
Alexander 1 Июл 2019 в 17:51

Вы можете использовать rename, например:

l_word = ['Data','Text']
df = df.rename(columns={ col:col+str(i+1) 
                         for word in l_word 
                         for i, col in enumerate(df.filter(like=word))})
2
Ben.T 1 Июл 2019 в 18:02