2015-07-05 3 views
0

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

mask = data['a'] == 4 
newData = data['c'][mask] 

Однако более сложные случаи, также возможно:

mask = ((data['a'] == 4) | (data['a'] == 8)) & ((data['b'] == 1) | (data['b'] == 5)) 
newData = data['c'][mask] 

Кроме того, несколько масок может потребоваться. Основная проблема заключается в том, что я не знаю заранее

  • сколько маски потребуются, и
  • сколько столбцов и
  • сколько значений в этих столбцах будут определять маски,

как эта информация будет предоставлена ​​пользователем.

Я думал, что я мог бы попросить пользователей создать входной файл вдоль этих линий:

# <maskName> - <columnName>: <columnValue(s)> - <columnName>: <columnValue(s)> - etc. 
maskA - a: 4, 8 - b: 1, 5 - c: 1 
maskB - a: 0, 8 - c: 2, 6, 10 

targetColumn: d 

я мог бы прочитать входной файл и цикл над ним. Правильно обработав линии, я мог бы определить количество необходимых масок, соответствующие столбцы, соответствующие значения и столбец, к которому должны применяться маски. Я мог бы также добавить эту информацию в списки и/или словари.

Однако я не уверен, как лучше всего решить проблему, когда я не знаю количество масок/столбцов/значений заранее и как создавать соответствующие маски, когда я их знаю. Любая помощь будет принята с благодарностью.

ответ

1

Поскольку вы можете передавать строки до df.query(), найти нужное подмножество очень просто, если вы можете преобразовать свой формат ввода в строку. Синтаксический анализатор, который я написал для вашего формата ввода, не является очень элегантным, но, надеюсь, вы получите эту идею:

import pandas as pd 
import numpy as np 

maskA_str = "maskA - a: 4, 8 - b: 1, 5 - c: 1" 
df = pd.DataFrame(
    {'a': np.random.randint(1, 10, 100), 
    'b': np.random.randint(1, 10, 100), 
    'c': np.random.randint(1, 10, 100)} 
) 

def create_query_str(mask_str): 
    mask_name, column_conds = mask_str.split('-')[0], mask_str.split('-')[1:] 
    query_str = '(' 
    column_strs =[] 
    for cond in column_conds: 
     cond_str = '(' 
     column, vals = cond.split(':') 
     column = column.strip() 
     test_strs = ['{c} == {v}'.format(c=column, v=val.strip()) 
        for val in vals.split(',')] 
     cond_str += ' | '.join(test_strs) 
     cond_str += ')' 
     column_strs.append(cond_str) 
    query_str += ' & '.join(column_strs) 
    query_str += ')' 
    return query_str 

create_query_str(maskA_str) 
Out[17]: '((a == 4 | a == 8) & (b == 1 | b == 5) & (c == 1))' 

# Can now be used directly in df.query() 
df.query(create_query_str(maskA_str)) 
+0

Спасибо, я не знал о методе запроса! Однако, к сожалению, кажется, что это не работает, когда значения являются строками, а не целыми числами (например, 'maskA - a: m, n - b: 1, 5 - c: 1'). В кадре данных строки будут отображаться как numpy dtype 'object', и кажется, что метод запроса не работает над ними. Будет ли хороший способ решить эту проблему? – Troas

+0

@Troas: Я думаю, что запрос все равно должен работать для строк, вам просто нужно указать строковые значения в строке запроса, например. '((a == 'm' | a == 'n'))' – Marius

+0

Вы абсолютно правы. Кажется странным цитировать строки в строке запроса, но это делает трюк. Благодаря! – Troas

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