2015-04-12 2 views
1

У меня есть два измерения, положение и температура, которые отбираются с фиксированной частотой дискретизации. Некоторые позиции могут возникать несколько раз в данных. Теперь я хочу построить температуру над положением, а не по времени. Вместо того, чтобы отображать две точки в одном и том же положении, я хочу заменить измерения температуры на среднее значение для данного местоположения. Как это можно сделать красиво в python с numpy?Python Numpy: Заменить повторяющиеся значения со средним значением

Мое решение до сих пор выглядит следующим образом:

import matplotlib.pyplot as plt 
import numpy as np 

# x = Position Data 
# y = Temperature Data 
x = np.random.permutation([0, 1, 1, 2, 3, 4, 5, 5, 6, 7, 8, 8, 9]) 
y = (x + np.random.rand(len(x)) * 1 - 0.5).round(2) 

# Get correct order 
idx = np.argsort(x) 
x, y = x[idx], y[idx] 
plt.plot(x, y) # Plot with multiple points at same location 

# Calculate means for dupplicates 
new_x = [] 
new_y = [] 
skip_next = False 
for idx in range(len(x)): 
    if skip_next: 
     skip_next = False 
     continue 
    if idx < len(x)-1 and x[idx] == x[idx+1]: 
     new_x.append(x[idx]) 
     new_y.append((y[idx] + y[idx+1])/2) 
     skip_next = True 
    else: 
     new_x.append(x[idx]) 
     new_y.append(y[idx]) 
     skip_next = False 

x, y = np.array(new_x), np.array(new_y) 
plt.plot(x, y) # Plots desired output 

Это решение не принимать во внимание, что некоторые позиции могут occoure более чем в два раза в данных. Чтобы заменить все значения, цикл должен запускаться несколько раз. Я знаю, что должно быть лучшее решение!

ответ

2

Один подход с использованием np.bincount -

import numpy as np 

# x = Position Data 
# y = Temperature Data 
x = np.random.permutation([0, 1, 1, 2, 3, 4, 5, 5, 6, 7, 8, 8, 9]) 
y = (x + np.random.rand(len(x)) * 1 - 0.5).round(2) 


# Find unique sorted values for x 
x_new = np.unique(x) 

# Use bincount to get the accumulated summation for each unique x, and 
# divide each summation by the respective count of each unique value in x 
y_new_mean= np.bincount(x, weights=y)/np.bincount(x) 

Sample пробег -

In [16]: x 
Out[16]: array([7, 0, 2, 8, 5, 4, 1, 9, 6, 8, 1, 3, 5]) 

In [17]: y 
Out[17]: 
array([ 6.7 , 0.12, 2.33, 8.19, 5.19, 3.68, 0.62, 9.46, 6.01, 
     8. , 1.07, 3.07, 5.01]) 

In [18]: x_new 
Out[18]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) 

In [19]: y_new_mean 
Out[19]: 
array([ 0.12 , 0.845, 2.33 , 3.07 , 3.68 , 5.1 , 6.01 , 6.7 , 
     8.095, 9.46 ]) 
+0

Выглядит хорошо и дает тот же точный результат - мое решение! Давайте посмотрим, если кто-то еще придумает другое хорошее решение, в противном случае я пойду и соглашусь с этим. – jrast

+0

@jrast Конечно! Не спешите. Просто любопытство в этом - Вы видите ускорение с таким подходом, если бы у вас была возможность сравниться? Я ожидаю хотя бы некоторого ускорения с этим. – Divakar

+0

Я просто сравнил его с решением @Will: я использовал набор данных с 300000 точками данных, каждый из которых занимает 3 раза (так что 100000 уникальных позиций.) Ваше решение: 10 циклов, лучше всего 3: 20,6 мс за цикл, Уилл Решение: 1 петля, лучше всего 3: 2,16 с за цикл. Я думаю, что мое оригинальное решение будет в том же диапазоне, что и Уилл, поскольку он также использует циклы. – jrast

0

Если я понимаю, что вы просите, вот один из способов сделать это, что гораздо проще.

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

data = np.random.permutation([(1, 5.6), (1, 3.4), (1, 4.5), (2, 5.3), (3, 2.2), (3, 6.8)]) 
>> array([[ 3. , 2.2], 
    [ 3. , 6.8], 
    [ 1. , 3.4], 
    [ 1. , 5.6], 
    [ 2. , 5.3], 
    [ 1. , 4.5]]) 

Мы можем сортировать и положить каждую позицию в словаре в качестве ключа, сохраняя при этом температуры для этой позиции в массиве в словаре. Мы используем некоторую обработку ошибок здесь, если ключ (позиция) еще не находится в нашем словаре, python будет жаловаться на KeyError, поэтому мы добавим его.

results = {} 
for entry in sorted(data, key=lambda t: t[0]): 
    try: 
     results[entry[0]] = results[entry[0]] + [entry[1]] 
    except KeyError: 
     results[entry[0]] = [entry[1]] 
print(results) 
>> {1.0: [3.3999999999999999, 5.5999999999999996, 4.5], 
2.0: [5.2999999999999998], 
3.0: [2.2000000000000002, 6.7999999999999998]} 

И с окончательным списком понимания мы можем сгладить это и получить результирующий массив.

np.array([[key, np.mean(results[key])] for key in results.keys()]) 
>> array([[ 1. , 4.5], 
    [ 2. , 5.3], 
    [ 3. , 4.5]]) 

Это можно поставить в функции:

def flatten_by_position(data): 
    results = {} 
    for entry in sorted(data, key=lambda t: t[0]): 
     try: 
      results[entry[0]] = results[entry[0]] + [entry[1]] 
     except KeyError: 
      results[entry[0]] = [entry[1]] 
    return np.array([[key, np.mean(results[key])] for key in results.keys()]) 

испытано с различными входами это решение должно быть достаточно быстрым для наборов данных при записи 1000000.

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