Подсчитайте количество результатов шкалы Лайкерта из нескольких вопросов столбца в пандах

У меня есть следующий кадр данных:

       Question1        Question2         Question3          Question4
User1  Agree            Agree          Disagree         Strongly Disagree
User2  Disagree         Agree          Agree            Disagree
User3  Agree            Agree          Agree            Agree

Есть ли способ преобразовать фрейм данных, указанный выше, в следующий?

              Agree         Disagree         Strongly Disagree
 Question1    2               1                  0

 Question2    2               1                  0

 Question3    2               1                  0
 Question4    1               1                  1

Это похоже на мой предыдущий вопрос: Создайте фрейм данных с сгруппированными вопросами из трех столбцов

Я попытался просмотреть предыдущие вопросы со стеком/поворотом, но не смог понять. Фактический фрейм данных содержит более 20 вопросов и шкалу Лайкерта от «полностью согласен», «согласен», «нейтрально», «не согласен», «категорически не согласен».


person yangd01234    schedule 06.06.2017    source источник


Ответы (2)


С pd.get_dummies

pd.get_dummies(df.stack()).groupby(level=1).sum()

           Agree  Disagree  Strongly Disagree
Question1      2         1                  0
Question2      3         0                  0
Question3      2         1                  0
Question4      1         1                  1

Переход на новый уровень
Мы можем использовать numpy.bincount, чтобы ускорить работу. Но мы должны обратить внимание на размеры

v = df.values
f, u = pd.factorize(v.ravel())
n, m = u.size, v.shape[1]
r = np.tile(np.arange(m), n)
b0 = np.bincount(r * n + f)
pad = np.zeros(n * m - b0.size, dtype=int)
b = np.append(b0, pad)

pd.DataFrame(b.reshape(m, n), df.columns, u)

           Agree  Disagree  Strongly Disagree
Question1      2         1                  0
Question2      3         0                  0
Question3      2         1                  0
Question4      1         1                  1

Еще один вариант numpy

v = df.values
n, m = v.shape
f, u = pd.factorize(v.ravel())

pd.DataFrame(
    np.eye(u.size, dtype=int)[f].reshape(n, m, -1).sum(0),
    df.columns, u
)

           Agree  Disagree  Strongly Disagree
Question1      2         1                  0
Question2      3         0                  0
Question3      2         1                  0
Question4      1         1                  1

Разница в скорости

%%timeit
v = df.values
f, u = pd.factorize(v.ravel())
n, m = u.size, v.shape[1]
r = np.tile(np.arange(m), n)
b0 = np.bincount(r * n + f)
pad = np.zeros(n * m - b0.size, dtype=int)
b = np.append(b0, pad)
​
pd.DataFrame(b.reshape(m, n), df.columns, u)
1000 loops, best of 3: 194 µs per loop

%%timeit
v = df.values
n, m = v.shape
f, u = pd.factorize(v.ravel())

pd.DataFrame(
    np.eye(u.size, dtype=int)[f].reshape(n, m, -1).sum(0),
    df.columns, u
)
1000 loops, best of 3: 195 µs per loop

%timeit pd.get_dummies(df.stack()).groupby(level=1).sum()
1000 loops, best of 3: 1.2 ms per loop
person piRSquared    schedule 06.06.2017
comment
Благодарю вас! Это сработало отлично, и ваша часть «дополнительного балла» из моего последнего вопроса помогла мне отсортировать столбцы. - person yangd01234; 07.06.2017

Вы можете перебирать столбцы с помощью pd.Series.value_counts. Если вы сделаете это с помощью apply, индексы будут выровнены автоматически:

df.apply(pd.Series.value_counts)
Out: 
                   Question1  Question2  Question3  Question4
Agree                    2.0        3.0        2.0          1
Disagree                 1.0        NaN        1.0          1
Strongly Disagree        NaN        NaN        NaN          1

Небольшая постобработка:

df.apply(pd.Series.value_counts).fillna(0).astype('int')
Out: 
                   Question1  Question2  Question3  Question4
Agree                      2          3          2          1
Disagree                   1          0          1          1
Strongly Disagree          0          0          0          1
person ayhan    schedule 06.06.2017