Преобразование массивов X и Y в сетку частот

#python #pandas #numpy #matrix #pivot-table

#python #панды #numpy #матрица #сводная таблица

Вопрос:

Я хотел бы преобразовать два массива (x и y) в матрицу частот n x n (n = 5), указав в каждой ячейке количество точек, которое содержит. Он заключается в повторной выборке обеих переменных на пять интервалов и подсчете существующего количества точек на ячейку.

Я пытался использовать сводную таблицу pandas, но не знаю, как ссылаться на каждую координату оси. Массивы X и Y — это две зависимые переменные, которые содержат значения от 0 до 100.

Я был бы очень признателен за чью-то помощь. Заранее большое вам спасибо.

Это пример кода:

 import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# Arrays example. They are always float type and ranging 0-100. (n_size array = 15)
x = 100 * np.random.random(15)
y = 100 * np.random.random(15)

# Df created for trying to pivot and counting values per cell
df = pd.DataFrame({'X':x,'Y':y})

# Plot the example data:
df.plot(x = 'X',y = 'Y', style = 'o')

  

Это то, что у меня есть:
введите описание изображения здесь

Это объектная матрица, сохраненная как df: введите описание изображения здесь

Ответ №1:

Если вам явно не нужно использовать pandas (чего вы не делаете, если речь идет только о частотной матрице), рассмотрите возможность использования numpy.histogram2d :

 # Sample data
x = 100*np.random.random(15)
y = 100*np.random.random(15)
  

Создайте свои ячейки (поскольку ваши ячейки x и y одинаковы, достаточно одного набора)

 bins = np.linspace(0, 100, 5 1)

# bins = array([  0.,  20.,  40.,  60.,  80., 100.])
  

Теперь используйте функцию гистограммы:

 binned, binx, biny = np.histogram2d(x, y, bins = [bins, bins])

# To get the result you desire, transpose
objmat = binned.T
  

Примечание: значения x привязаны к первому измерению (ось 0), что визуально означает «вертикальный». Отсюда и транспонирование.

Построение графиков:

 fig, ax = plt.subplots()
ax.grid()
ax.set_xlim(0, 100)
ax.set_ylim(0, 100)

ax.scatter(x, y)
for i in range(objmat.shape[0]):
    for j in range(objmat.shape[1]):
        c = int(objmat[::-1][j,i])
        ax.text((bins[i] bins[i 1])/2, (bins[j] bins[j 1])/2, str(c), fontdict={'fontsize' : 16, 'ha' : 'center', 'va' : 'center'})
  

Результат:
введите описание изображения здесь

Комментарии:

1. Я выбрал этот ответ, потому что он показался мне самым простым и позволяет изменять ‘n’, но все ответы фантастические. Спасибо всем вам, ребята!

Ответ №2:

Вы могли бы использовать GroupBy.size сопоставление групповых осей с центром каждой сетки. Затем вы можете использовать Axes.text их для рисования

 import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(20)


max_val = 100
n = 5
len_group = max_val // 5
x = max_val * np.random.random(15)
y = max_val * np.random.random(15)


# Df created for trying to pivot and counting values per cell
df = pd.DataFrame({'X':x,'Y':y})



x_groups = df['X'] // len_group * len_group   len_group / 2
y_groups = df['Y'] // len_group * len_group   len_group / 2

fig, ax= plt.subplots(figsize=(13, 6))

ax.set_ylim(0, max_val)
ax.set_xlim(0, max_val)

df.plot(x = 'X',y = 'Y', style = 'o', ax=ax)
for i, val in df.groupby([x_groups, y_groups]).size().items():
    ax.text(*i, val,fontdict={'fontsize' : 20, 'ha' : 'center',  'va':'center'})
plt.grid()
  

введите описание изображения здесь

Ответ №3:

Вы можете просто создать ячейки с pd.cut помощью, а затем разархивировать groupby ячейки по X переменной, и у вас будет матрица подсчетов частот.

 df['Xc'] = pd.cut(df['X'], range(0, 101, 20))
df['Yc'] = pd.cut(df['Y'], range(0, 101, 20))

mat = df.groupby(['Xc', 'Yc']).size().unstack('Xc')
mat
  
 Xc         (0, 20]  (20, 40]  (40, 60]  (60, 80]  (80, 100]
Yc                                                         
(0, 20]          0         1         1         0          0
(20, 40]         4         0         1         2          0
(40, 60]         0         0         0         0          0
(60, 80]         3         0         1         0          0
(80, 100]        1         0         1         0          0
  

Ответ №4:

Элегантного решения проблемы построения графика не существует. Но вот что вы можете сделать.

 # Calculate the counts
counts = df.groupby([df.X.astype(int) // 20, 
                     df.Y.astype(int) // 20]).size().astype(str)
# Restore the original scales
counts.index = pd.MultiIndex.from_tuples([(x * 20   10, 
                                           y * 20   10) 
                    for x,y in counts.index.to_list()], 
                    names=counts.index.names)
fig = plt.figure()
ax = fig.add_subplot(111)
# Plot the text labels
[ax.text(*xy, txt) for (xy, txt) in counts.items()]
# Update the axes extents
ax.axis([0, counts.index.levels[0].max()   10, 
         0, counts.index.levels[1].max()   10])

plt.show()
  

введите описание изображения здесь

Ответ №5:

 import pandas as pd
import numpy as np
import seaborn as sns

sns.set_style("whitegrid")
# Arrays example. They are always float type and ranging 0-100. (n_size array = 15)
x = 100 * np.random.random(15)
y = 100 * np.random.random(15)

# Df created for trying to pivot and counting values per cell
df = pd.DataFrame({'X':x,'Y':y})

ir = pd.interval_range(start=0, freq=20, end=100, closed='left')

df['xbin'] = pd.cut(df['X'], bins=ir)
df['ybin'] = pd.cut(df['Y'], bins=ir)

df['xbin'] = df['xbin'].apply(lambda x: x.mid)
df['ybin'] = df['ybin'].apply(lambda x: x.mid)

fig, ax= plt.subplots()

ax.set_ylim(0, 100)
ax.set_xlim(0, 100)

for i, val in df.groupby(['xbin', 'ybin']).size().items():
    if val!=0:
        ax.text(*i, val,fontdict={'fontsize' : 20, 'ha' : 'center', 'va' : 'center'})
  

введите описание изображения здесь

Ответ №6:

Одним из вариантов является вызов np.add.at ravel матрицы частот

     x = 100 * np.random.random(15)
    y = 100 * np.random.random(15)        
    n = 5
    points = (np.array([x, y]) / 20).astype(int)

    z = np.zeros((n, n), dtype=int)
    np.add.at(z.ravel(), 
              np.ravel_multi_index(points, z.shape), 
              np.ones(points.shape[1]))
  

Пример запуска:

 print(points)
print(z)
[[0 0 0 2 4 1 2 1 1 0 1 1 3 0 0]
 [0 0 1 4 0 4 1 0 1 3 3 1 0 0 3]]
[[3 1 0 2 0]
 [1 2 0 1 1]
 [0 1 0 0 1]
 [1 0 0 0 0]
 [1 0 0 0 0]]