2016-05-31 2 views
1

Я ищу лучшее решение проблемы обработки данных, с которой я столкнулся в своих исследованиях. У меня есть Pandas DataFrame, и я пытаюсь извлечь все значения определенного столбца (Z) внутри группы (Frame), сохраняя пары ID, заданные другим столбцом (Name). Конечный результат не должен быть объектом Панды, но было бы неплохо сделать это полностью в Пандах. Задача наиболее ясна с примера.Как обрабатывать строки строк Сохранение идентификационной колонки без словарей с использованием Pandas?

d=[['7500', '3.2900', '0', 'apple'],['7500', '-0.3500', '1', 'orange'],['7500', '-4.1400', '2', 'orange'],['7501', '3.4625', '0', 'apple'],['7501', '-0.2275', '1', 'apple'],['7501', '-4.1175', '2', 'orange'],['7502', '3.2087', '0', 'orange'],['7502', '-0.7313', '1', 'apple'],['7502', '-4.7513', '2', 'apple']] 
df=pd.DataFrame(d, columns=["Frame","Z","Order","Name"]) 


>>> df 
    Frame  Z Order Name 
0 7500  3.2900 0 apple 
1 7500 -0.3500 1 orange 
2 7500 -4.1400 2 orange 
3 7501  3.4625 0 apple 
4 7501 -0.2275 1 apple 
5 7501 -4.1175 2 orange 
6 7502  3.2087 0 orange 
7 7502 -0.7313 1 apple 
8 7502 -4.7513 2 apple 

Таким образом, для каждой группы кадров, я хотел бы взять уникальные комбинации, основанные на колонке «Заказ», поэтому для кадров 7500 группы, которая будет:

(0,1) 
(0,2) 
(1,2) 

Но обратите внимание, что количество строк в каждой группе может варьироваться от 1 до 5, а не только 0, 1, 2. Тогда я бы следить за значением «Name» из этих пар,

(apple, orange) 
(apple, orange) 
(orange, orange) 

Тогда я вытаскивать значения «Z» этих соответствующих пар, как таковые:

(3.2900, -0.3500) 
(3.2900, -4.1400) 
(-0.3500, -4.1400) 

И теперь, в конце, объединяя эти данные, я хочу два списка для каждой уникальной пары на основе столбца Name. Мы будем называть эти списки first_vals и second_vals для этого примера, но это совершенно произвольно,

(apple, orange) 
first_vals = [3.2900, 3.2900, ...] 
second_vals = [-0.3500, -4.1400, ...] 

(orange, orange) 
first_vals = [-0.3500, ...] 
second_vals = [-4.140, ...] 

Вот одно решение, я пришел с использованием словарей. Он работает, но я считаю его довольно уродливым и скрывает структуру данных в ключе словаря. Этот пример основан на том, что группа была предварительно отсортирована по столбцу «Заказ», но на самом деле проблем нет.

from itertools import combinations 
from collections import defaultdict 

zpairs = defaultdict(list) 

for name, group in df.groupby(["Frame"]): 
    order_pairs = combinations(range(len(group)), 2) 
    zvals = group["Z"].values 
    rowids = group["Name"].values 
    for pair in order_pairs: 
     pair_str = str(rowids[pair[0]])+"-"+str(rowids[pair[1]]) 
     zpairs[pair_str+"-first"].append(zvals[pair[0]]) 
     zpairs[pair_str+"-second"].append(zvals[pair[1]]) 

Результат этого код выглядит следующим образом:

>>> dict(zpairs) 

{'apple-apple-first': ['3.4625', '-0.7313'], 
'apple-apple-second': ['-0.2275', '-4.7513'], 
'apple-orange-first': ['3.2900', '3.2900', '3.4625', '-0.2275'], 
'apple-orange-second': ['-0.3500', '-4.1400', '-4.1175', '-4.1175'], 
'orange-apple-first': ['3.2087', '3.2087'], 
'orange-apple-second': ['-0.7313', '-4.7513'], 
'orange-orange-first': ['-0.3500'], 
'orange-orange-second': ['-4.1400']} 

Есть ли способ, чтобы обработать мой dataframe, который не зависит от словарей и не использует словарь ключа для хранения данных? Это не совсем о повышении производительности, но это было бы полезно.

+0

Что вы надеетесь достичь, избегая использования словарей? Что вы подразумеваете под «скрывает структуру данных в ключе словаря»? – andrew

ответ

1

Вот панды подход, который осуществляется в два этапа:

  1. Получить пары имен и значений Z для каждой группы кадров.
  2. Объедините компоненты значения Z для каждой пары имен.

Шаг 1

Чтобы получить пар имен и значений г, я собираюсь написать вспомогательную функцию get_group_pairs, что я буду звонить, когда я выполняю groupby. Я делаю подобный процесс к тому, что вы делаете в цикле, но возвращение выхода в DataFrame:

def get_group_pairs(grp): 
    pairs = combinations(grp.index, 2) 
    data = [grp.loc[p, ('Name', 'Z')].values.flatten('F') for p in pairs] 
    return pd.DataFrame(data, columns=['Name1', 'Name2', 'Z1', 'Z2']) 

namepairs = df.groupby('Frame').apply(get_group_pairs).reset_index(level=1, drop=True) 

reset_index делается исключительно для удаления ненужного уровня индекса, поэтому промежуточный результат выглядит красиво. Это не обязательно, если вы не заботитесь о промежуточном выходе.Промежуточный выходной namepairs:

 Name1 Name2  Z1  Z2 
Frame         
7500 apple orange 3.2900 -0.3500 
7500 apple orange 3.2900 -4.1400 
7500 orange orange -0.3500 -4.1400 
7501 apple apple 3.4625 -0.2275 
7501 apple orange 3.4625 -4.1175 
7501 apple orange -0.2275 -4.1175 
7502 orange apple 3.2087 -0.7313 
7502 orange apple 3.2087 -4.7513 
7502 apple apple -0.7313 -4.7513 

Шаг 2

же вспомогательная функция/groupby/apply узор, как на стадии 1. По существу, я просто группа, используя оба имени, а затем преобразовать два Z столбцы списки:

def merge_zpairs(grp): 
    data = {'Z1': grp['Z1'].tolist(), 'Z2': grp['Z2'].tolist()} 
    return pd.Series(data) 

zpairs = namepairs.groupby(['Name1', 'Name2']).apply(merge_zpairs).reset_index() 

Опять же, reset_index не является строго необходимым. Без него вы получите MultiIndex, состоящий из пар имен. Это дает конечный результат zpairs:

Name1 Name2         Z1         Z2 
0 apple apple     [3.4625, -0.7313]     [-0.2275, -4.7513] 
1 apple orange [3.2900, 3.2900, 3.4625, -0.2275] [-0.3500, -4.1400, -4.1175, -4.1175] 
2 orange apple     [3.2087, 3.2087]     [-0.7313, -4.7513] 
3 orange orange       [-0.3500]        [-4.1400] 

Комбинированный код

Для удобства, вот код из шагов 1 и 2 в сочетании:

def get_group_pairs(grp): 
    pairs = combinations(grp.index, 2) 
    data = [grp.loc[p, ('Name', 'Z')].values.flatten('F') for p in pairs] 
    return pd.DataFrame(data, columns=['Name1', 'Name2', 'Z1', 'Z2']) 

def merge_zpairs(grp): 
    data = {'Z1': grp['Z1'].tolist(), 'Z2': grp['Z2'].tolist()} 
    return pd.Series(data) 

namepairs = df.groupby('Frame').apply(get_group_pairs).reset_index(level=1, drop=True) 
zpairs = namepairs.groupby(['Name1', 'Name2']).apply(merge_zpairs).reset_index() 

Получение Словарь

Вы можете найти похожие слова structu но не идентичны, с некоторыми незначительными изменениями в коде шага 2. По существу, вместо того, чтобы использовать reset_index использовать to_dict с orient='index':

zpairs = namepairs.groupby(['Name1', 'Name2']).apply(merge_zpairs) 
zpairs_dict = zpairs.to_dict(orient='index') 

Это приводит к словарю словарей: первый ключ является парой имен, то второй ключ является значением Z вы хотите. Например, синтаксис для 'apple-apple-first' будет:

zpairs_dict[('apple', 'apple')]['Z1'] 
+0

Этот ответ был именно тем, что я искал. Функция «get_group_pairs» очень крутая, спасибо! Тем не менее, я знаю, что я сказал, что производительность не является основным фактором, но для моего большого фрейма данных похоже, что метод словаря примерно в 10 раз быстрее. Я предполагаю, что структура применения/промежуточного списка действительно мешает этому подходу. Очень образованный, хотя! – cing

 Смежные вопросы

  • Нет связанных вопросов^_^