Я намагаюся згрупувати один кадр даних на одномустовпець, утримуючи декілька стовпців з одного рядка в кожній групі і об'єднуючи рядки з інших рядків у декілька стовпців на основі значення одного стовпця. Ось приклад ...
df = pd.DataFrame({"test" : ["a","a","a","a","a","a","b","b","b","b"],
"name" : ["aa","ab","ac","ad","ae","ba","bb","bc","bd","be"],
"amount" : [1, 2, 3, 4, 5, 6, 7, 8, 9, 9.5],
"role" : ["x","y","y","x","x","z","y","y","z","y"]})
дф
amount name role test
0 1.0 aa x a
1 2.0 ab y a
2 3.0 ac y a
3 4.0 ad x a
4 5.0 ae x a
5 6.0 ba z a
6 7.0 bb y b
7 8.0 bc y b
8 9.0 bd z b
9 9.5 be y b
Хотілося б групувати на тесті, зберігати ім'я ісума, коли role = "z", створює стовпець (назвемо його X), який об'єднує значення імені, коли role = "x" і інший стовпець (давайте назвемо його Y), що об'єднує значення імені при ролі = "y". [Копіровані значення розділені «;»] Можуть бути нульовими багато рядків з роллю = "х", нуль до багатьох рядків з роллю = "у" і одним рядком з роллю = "z" за значенням тесту. Для X і Y вони можуть бути нульовими, якщо для цього тесту немає рядків. Значення суми скидається для всіх рядків з роллю = "x" або "y". Бажаний результат буде таким:
test name amount X Y
0 a ba 6.0 aa; ad; ae ab; ac
1 b bd 9.0 None bb; bc; be
Для конкатенуючої частини, я знайшов x.ix[x.role == "x", X] = "{%s}" % "; ".join(x["name"])
, які я міг би повторити для y. Я спробував кілька речей по лінії name = x[x.role == "z"].name.first()
для назви та суми. Я також спробував спустити обидва шляхи певної функції і лямбда-функцію без успіху. Оцініть будь-які думки.
Відповіді:
1 для відповіді № 1Ви можете створювати спеціальні стовпці в apply
функція після groupby
наступним чином де g
може розглядатися як субкадр з одним значенням у тестовому стовпці, і оскільки потрібно повернути кілька стовпців, потрібно створити a Series
Об'єкт для кожної групи, де індекси є відповідними заголовками в результаті:
df.groupby("test").apply(lambda g: pd.Series({"name": g["name"][g.role == "z"].iloc[0],
"amount": g["amount"][g.role == "z"].iloc[0],
"X": "; ".join(g["name"][g.role == "x"]),
"Y": "; ".join(g["name"][g.role == "y"])
})).reset_index()
1 для відповіді № 2
# set index and get crossection where test is "z"
z = df.set_index(["test", "role"]).xs("z", level="role")
# get rid of "z" rows and group by "test" and "role" to join names
xy = df.query("role != "z"").groupby(["test", "role"])["name"].apply(";".join).unstack()
# make columns of xy upper case
xy.columns = xy.columns.str.upper()
pd.concat([z, xy], axis=1).reset_index()