2016-09-02 5 views
1

Вот версия tl; dr того, что мне нужно; подробности приведены ниже: SQL-запрос дает мне таблицу с полями [person 1 id], [person 2 id] и [количество раз, когда они были в группе вместе]. Я хочу преобразовать в рамку данных pandas, которая является квадратной - по одной строке на человека и по одному столбцу на человека, причем значение каждого элемента будет числом раз в группе. Я ищу более элегантный способ сделать это, чем перебирать строки моего результата и заполнять каждый элемент данных за один раз.Преобразование результата SQL из самосоединения в квадратный файл данных pandas


У меня есть база данных с таблицей заданий, которая имеет столбец для person_id и колонку для assignment_id. Он имеет и другие вещи тоже, но для наших целей здесь, это то, что имеет значение:

SELECT person_id, assignment_id FROM assignments; 
 
person_id | assignment_id 
----------+-------------- 
     385 |   42 
     163 |   29 
     51 |   42 
     385 |   37 
     163 |   37 
     ... 

Я хочу видеть, как часто любые два человека были на том же задании. Так что я:

SELECT a1.person_id AS p1_id, a2.person_id AS p2_id, COUNT(*) 
FROM assignments AS a1 
INNER JOIN assignments AS a2 ON a1.assignment_id = a2.assignment_id AND a1.person_id < a2.person_id 
GROUP BY a1.person_id, a2.person_id 

Что дает выход как:

 
p1_id | p2_id | count 
------+-------+------ 
    51 | 385 | 1 
    163 | 385 | 1 
     ... 

Теперь я создаю сценарий Python, чтобы получить доступ к данным и хотите сбросить его в dataframe с рядом для каждого человека, столбец для каждого человека, а ячейка имеет количество раз, когда они разделяли назначение. Таким образом, вывод будет примерно таким (мне все равно, что происходит в * ячейках), может быть 0 или количество назначений, которые сделал человек, - и не волнует, как первая строка и столбец отформатирован):

 
p1_id | p_51 | p_163 | p_385 
-------+--------+--------+-------- 
    51 | * | 0 | 1 
    163 | 0 | * | 1 
    385 | 1 | 1 | * 

Я только около 20 человек, так что не мешало бы производительность измеримо просто установить значения по одному, но я пытаюсь научиться хорошо практику, когда у меня больше наборы данных. Каков правильный способ сделать что-то подобное?

(Я открыт для изменения запроса SQL, если это лучший способ справиться с этим.)

ответ

1

Вы можете использовать groupby после преобразования требуемых столбцов типа str и агрегата путем присоединения к ним, а также принятию их счет.

df[['person_id', 'assignment_id']] = df[['person_id', 'assignment_id']].astype(str) 

df = df.groupby(['assignment_id'], as_index=False, sort=False)['person_id'] \ 
     .agg({'col':','.join})['col']           \ 
     .str.split(',').apply(lambda x: sorted(x, reverse=True))    \ 
     .apply(pd.Series).add_prefix('p_id_')         \ 
     .set_index('p_id_0', drop=False) 

Вы могли бы упростить далее с помощью get_dummies для получения индикаторных переменных для индекса, p_id_0, как показан:

df1 = pd.get_dummies(df['p_id_1']).add_prefix('p_') 
print (df1) 

     p_163 p_385 
p_id_0    
51  0.0 1.0 
163  0.0 0.0 
385  1.0 0.0 

df2 = pd.get_dummies(df['p_id_0']).add_prefix('p_') 
print (df2) 

     p_163 p_385 p_51 
p_id_0      
51  0.0 0.0 1.0 
163  1.0 0.0 0.0 
385  0.0 1.0 0.0 

Затем конкатенации этих отдельных dataframes после отображения всех значений индексируемого кадра 0 ' после чего группируется одна и та же колонка с именами столбцов:

df_final = pd.concat([df1, df2.applymap(lambda x: 0)], axis=1).add_prefix('p_') 
print (df_final.groupby(df.columns, axis=1).sum()) 

     p_163 p_385 p_51 
p_id_0      
51  0.0 1.0 0.0 
163  0.0 0.0 0.0 
385  1.0 0.0 0.0 
+1

Отлично. Благодаря! –