2016-02-10 3 views
2

У меня есть огромный набор данных, где мне нужно хрустеть много цифр и искать 1. для реального решения и 2. для быстрого.Pandas: вычитание двух серий с конкретным соответствием индексу

Я попытался упростить и перенести свою проблему на пример реального мира как можно лучше, надеюсь, это ясно. Я уверен (по крайней мере, надеюсь), это как-то обычная проблема в Пандах, а не очень особенная.

Итак, у меня есть два сотрудника в компании под названием foo и bar. Иногда они работают в один и тот же день и повторяют одну и ту же задачу снова и снова. Я измеряю время, необходимое для выполнения задачи (иногда только один раз в день, иногда несколько раз).

Что я ищу сейчас, это разница между кратчайшими моментами, если оба они работали в тот же день.

Я стараюсь использовать метод непросеянной муки, так что работа с таблицей (и не повторение) в максимально возможной степени.

Моя текущая стратегия: сгруппировать данные по day и name, держать только самый короткий time за day и name, если размер группы 2 (что означает, что я есть данные для обоих работников в тот же день) вычитают оба раза.

Конечная цель: имеющее Series отличия в раскраске кратчайшие.

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

Ниже приведен пример набора данных:

from StringIO import StringIO 
import pandas as pd 

raw_data="""day name time 
1 foo 10 
1 foo 9 
1 bar 4 
2 foo 12 
2 foo 13 
3 bar 3 
3 bar 5 
5 foo 8 
5 bar 5 
5 foo 9 
5 bar 1 
""" 

df = pd.read_csv(StringIO(raw_data), sep=' ') 

grouped_by_day_and_name = df.groupby(['day', 'name']) 

Это как таблица выглядит как после группировки и сохраняя только кратчайшее время:

print grouped_by_day_and_name.agg({'time': min}) 

      time 
day name  
1 bar  4 
    foo  9 
2 foo  12 
3 bar  3 
5 bar  1 
    foo  8 

Теперь я заинтересован только в день 1 и 5, так как это единственные дни, у меня есть данные как для bar, так и для foo. Поэтому я бы закончил, если бы я каким-то образом мог фильтровать данные и вычитать оба раза в каждой группе, поэтому результат будет [-5, -7] (со дня 1: 4-9, 5 день 1-8).

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

foo_best_times = df[df.name == 'foo'].groupby(['day', 'name']).agg({'time': min}) 
bar_best_times = df[df.name == 'bar'].groupby(['day', 'name']).agg({'time': min}) 

После попытки вычесть каждый один:

print foo_best_times - bar_best_times 

      time 
day name  
1 bar NaN 
    foo NaN 
2 foo NaN 
3 bar NaN 
5 bar NaN 
    foo NaN 

Что я задался для что-то вроде этого:

day time 
1 -5 
2 NaN 
3 NaN 
5 -7 

Как бы я вычесть оба ряда, сопоставив только индекс day как индекс?

Это даже правильный подход, чтобы сделать это быстро?

+0

Может ли только когда-либо быть два сотрудника вашей аналогии? –

+0

Возможно, вы можете сбросить индекс, например, 'print foo_best_times.reset_index (level = 1) ['time'] - bar_best_times.reset_index (level = 1) ['time']' – jezrael

+0

В принципе, могут быть и несколько сотрудников, однако я стараюсь выяснить этот случай сам. Это было бы уже огромно, если бы я решил проблему с двумя рабочими ;-) – tamasgal

ответ

2

Я думаю, что вы можете использовать pivot_table с aggfunc=min и затем вычитать колонн bar и foo:

from StringIO import StringIO 
import pandas as pd 

raw_data="""day name time 
1 foo 10 
1 foo 9 
1 bar 4 
2 foo 12 
2 foo 13 
3 bar 3 
3 bar 5 
5 foo 8 
5 bar 5 
5 foo 9 
5 bar 1 
""" 

df = pd.read_csv(StringIO(raw_data), sep=' ') 
print df 
    day name time 
0  1 foo 10 
1  1 foo  9 
2  1 bar  4 
3  2 foo 12 
4  2 foo 13 
5  3 bar  3 
6  3 bar  5 
7  5 foo  8 
8  5 bar  5 
9  5 foo  9 
10 5 bar  1 
df = df.pivot_table(index='day', columns='name', values='time', aggfunc=min) 

print df 
name bar foo 
day   
1  4 9 
2  NaN 12 
3  3 NaN 
5  1 8 


print df['bar'] - df['foo'] 
1 -5 
2 NaN 
3 NaN 
5 -7 
dtype: float64 
+0

Это очень быстро! И он также работает с несколькими «сотрудниками». – tamasgal

1

Я думаю, что вы хотите сделать, это «внутреннее» соединение.Этот тип соединения выполняет индекс, соответствующий вы ищете:

from StringIO import StringIO 
import pandas as pd 

raw_data="""day name time 
1 foo 10 
1 foo 9 
1 bar 4 
2 foo 12 
2 foo 13 
3 bar 3 
3 bar 5 
5 foo 8 
5 bar 5 
5 foo 9 
5 bar 1 
""" 

df = pd.read_csv(StringIO(raw_data), sep=' ') 

# Split the dataset into the two workers 
foo = df.query('name == "foo"') 
bar = df.query('name == "bar"') 

# Find for each day the shortest working time 
foo = foo.groupby('day').agg('min') 
bar = bar.groupby('day').agg('min') 

# Perform an inner join of the two workers, this only keeps days 
# where both workers have been working 
joined = foo.join(bar, how='inner', lsuffix='_foo', rsuffix='_bar') 

# Compute the difference in minimum working times 
diff = joined['time_bar'] - joined['time_foo'] 

print diff 

Результат:

day 
1 -5 
5 -7 
dtype: int64 

Если вы хотите NaN «s на те дни, когда работал только один рабочий, вы можете выполнить» внешний»присоединиться:

# Perform an outer join of the two workers, this only keeps days 
# where both workers have been working 
joined = foo.join(bar, how='outer', lsuffix='_foo', rsuffix='_bar') 

# Compute the difference in minimum working times 
diff = joined['time_bar'] - joined['time_foo'] 

print diff 

Результат:

day 
1 -5 
2 NaN 
3 NaN 
5 -7 
dtype: float64 
Смежные вопросы