Объединять панды DataFrame столбцы, начинающиеся с одинаковых букв - PullRequest
7 голосов
/ 07 июня 2019

Допустим, у меня есть DataFrame:

>>> df = pd.DataFrame({'a1':[1,2],'a2':[3,4],'b1':[5,6],'b2':[7,8],'c':[9,0]})
>>> df
   a1  a2  b1  b2  c
0   1   3   5   7  9
1   2   4   6   8  0
>>> 

И я хочу объединить (возможно, не объединить, а объединить) столбцы, в которых первая буква их имени равна, например a1 иa2 и другие ... но, как мы видим, есть столбец c, который сам по себе не имеет других аналогичных столбцов, поэтому я хочу, чтобы они не выдавали ошибок, вместо этого добавляли к ним NaN s.

Я хочу объединить таким образом, чтобы он превратил широкий DataFrame в длинный DataFrame, в основном, как широкий или длинный вариант.

У меня уже есть решение проблемы,но единственное, что это очень неэффективно, я хотел бы более эффективное и более быстрое решение (в отличие от моего: P), у меня в настоящее время есть цикл for и код try except (тьфу, уже плохо звучит), такойкак:

>>> df2 = pd.DataFrame()
>>> for i in df.columns.str[:1].unique():
    try:
        df2[i] = df[[x for x in df.columns if x[:1] == i]].values.flatten()
    except:
        l = df[[x for x in df.columns if x[:1] == i]].values.flatten().tolist()
        df2[i] = l + [pd.np.nan] * (len(df2) - len(l))


>>> df2
   a  b    c
0  1  5  9.0
1  3  7  0.0
2  2  6  NaN
3  4  8  NaN
>>> 

Я хотел бы получить те же результаты с лучшим кодом.

Ответы [ 7 ]

4 голосов
/ 07 июня 2019

Я бы порекомендовал melt, затем pivot.Чтобы разрешить дубликаты, вам нужно повернуть столбец с накоплением.

u = df.melt()
u['variable'] = u['variable'].str[0]  # extract the first letter
u.assign(count=u.groupby('variable').cumcount()).pivot('count', 'variable', 'value')

variable    a    b    c
count                  
0         1.0  5.0  9.0
1         2.0  6.0  0.0
2         3.0  7.0  NaN
3         4.0  8.0  NaN

Это можно переписать как,

u = df.melt()
u['variable'] = [x[0] for x in u['variable']]
u.insert(0, 'count', u.groupby('variable').cumcount())

u.pivot(*u)

variable    a    b    c
count                  
0         1.0  5.0  9.0
1         2.0  6.0  0.0
2         3.0  7.0  NaN
3         4.0  8.0  NaN

Если производительность имеет значение, вотальтернатива с pd.concat:

from operator import itemgetter

pd.concat({
    k: pd.Series(g.values.ravel()) 
    for k, g in df.groupby(operator.itemgetter(0), axis=1)
}, axis=1)

   a  b    c
0  1  5  9.0
1  3  7  0.0
2  2  6  NaN
3  4  8  NaN
3 голосов
/ 07 июня 2019

Используйте словарь:

df = pd.DataFrame({i: pd.Series(x.to_numpy().ravel()) 
                      for i, x in df.groupby(lambda x: x[0], axis=1)})
print (df)
   a  b    c
0  1  5  9.0
1  3  7  0.0
2  2  6  NaN
3  4  8  NaN
3 голосов
/ 07 июня 2019

Мы можем попробовать групповые столбцы (axis=1):

def f(g,a):
    ret = g.stack().reset_index(drop=True)
    ret.name = a
    return ret

pd.concat( (f(g,a) for a,g in df.groupby(df.columns.str[0], axis=1)), axis=1)

вывод:

    a   b   c
0   1   5   9.0
1   3   7   0.0
2   2   6   NaN
3   4   8   NaN
2 голосов
/ 07 июня 2019

Я знаю, что это не так хорошо, как использование расплава, но, поскольку этот переход в одну строку, если вам нужно более быстрое решение, попробуйте решение cs95

df.groupby(df.columns.str[0],1).agg(lambda x : x.tolist()).sum().apply(pd.Series).T
Out[391]: 
     a    b    c
0  1.0  5.0  9.0
1  3.0  7.0  0.0
2  2.0  6.0  NaN
3  4.0  8.0  NaN
1 голос
/ 07 июня 2019

Это решение дает аналогичный ответ CS95 и в два-три раза быстрее.

grouping = df.columns.map(lambda s: int(s[1:]) if len(s) > 1 else 1)
df.columns = df.columns.str[0]   # Make a copy if the original dataframe needs to be retained
result = pd.concat((g for _, g in df.groupby(grouping, axis=1)), 
                   axis=0, ignore_index=True, sort=False)

Вывод

    a   b   c
0   1   5   9.0
1   2   6   0.0
2   3   7   NaN
3   4   8   NaN
1 голос
/ 07 июня 2019

Использование pd.concat с pd.melt и pd.groupby:

pd.concat([d.T.melt(value_name=k)[k] for k, d in df.groupby(df.columns.str[0], 1)], 1)

Вывод:

   a  b    c
0  1  5  9.0
1  3  7  0.0
2  2  6  NaN
3  4  8  NaN
1 голос
/ 07 июня 2019

Использование rename и groupby.apply:

df = (df.rename(columns = dict(zip(df.columns, df.columns.str[:1])))
        .groupby(level=0, axis=1, group_keys=False)
        .apply(lambda x: pd.DataFrame(x.values.flat, columns=np.unique(x.columns))))

print(df)
   a  b    c
0  1  5  9.0
1  3  7  0.0
2  2  6  NaN
3  4  8  NaN
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...