将 X 和 Y 数组转换为频率网格
Posted
技术标签:
【中文标题】将 X 和 Y 数组转换为频率网格【英文标题】:Convert X and Y arrays into a frequencies grid 【发布时间】:2021-02-06 18:00:46 【问题描述】:我想将两个数组(x和y)转换成一个频率 nxn 矩阵(n = 5),表示每个单元格包含的点数。它包括将两个变量重新采样为五个区间并计算每个单元格的现有点数。
我尝试过使用 pandas pivot_table,但不知道引用每个轴坐标的方式。 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')
结果:
【讨论】:
我选择了这个答案,因为在我看来它是最简单的一个,并且允许使用不同的“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
创建bin,然后使用groupby
创建bin,然后沿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】:一种选择是在频率矩阵的ravel
上调用np.add.at
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]]
【讨论】:
以上是关于将 X 和 Y 数组转换为频率网格的主要内容,如果未能解决你的问题,请参考以下文章