在scikit-learn随机森林模型中对多行数据进行分组
我在表达我的问题时遇到了一些困难,如果有什么不清楚的地方,请告诉我。我最终的目标是用机器学习模型来确定一个射频发射器的位置。虽然还有很多其他方法可以用来识别射频信号的来源(比如三角测量和多个接收器之间的时间差),但我想看看能否用机器学习模型来实现这个目标。
我正在尝试使用随机森林分类器,这是来自scikit-learn的一个工具,来构建一个模型,以识别射频信号的来源。这个模型是基于在一个已知区域内多个接收器的信号强度。这些接收器通过网络连接到一个中央数据库。接收器的位置是固定的,但发射器可能在任何地方,因此接收器接收到的信号强度主要取决于发射器是否与接收器之间有直接的视线。接收器的信号强度范围是1到255。如果是0,说明接收器没有接收到信号,因此不会记录在数据库中。这个不被记录的情况稍后会很重要。信号强度为255表示接收器接收到的信号非常强。
数据库每秒记录一次接收器的数据(请看下面的表格)。每组time
代表在那个时间点每个接收器接收到的信号情况。如前所述,如果某个接收器没有接收到信号,它就不会被记录在数据库中,因此每组time
可能只有1行,也可能有多达X
行,X
代表系统中接收器的总数(例如,如果有十个接收器在同一频率上监听,且每个接收器都接收到信号,那么数据库中会记录十个接收器的行;但如果只有其中三个接收到信号,数据库中只会记录三行)。基本上,我想把数据库中信号强度的数据与已知位置进行关联。例如,如果Red
和Green
的信号强度很强,说明信号可能来自Foo
;而如果Red
和Yellow
的信号强度很强,但Blue
的信号很弱,说明信号可能来自Bar
。已知的location
数据是通过观察发射器在已知位置时的信号情况手动构建的,这个过程非常繁琐。
接收器数据的记录方式(跨多行且不知道数据集中会出现多少行)给我在建模时带来了明显的挑战,因为RandomForestClassifier
是逐行查看数据的。我需要按日期/时间对数据进行分组,但不知道在任何给定时间会有多少接收器接收到信号,这让我很难以更合理的方式对数据进行建模。至少到目前为止,我还没有想到什么好的主意。
下面的表格包含了来自已知位置(Region A
)的几秒钟的信号数据。有没有人能给我一些建议,如何重构这些数据,以便能与scikit-learn的RandomForestClassifier
一起使用?
接收器名称 | 时间 | RSSI | 位置 |
---|---|---|---|
Red | 2024-03-21 20:37:58 | 182 | Region A |
Blue | 2024-03-21 20:37:58 | 254 | Region A |
Green | 2024-03-21 20:37:58 | 208 | Region A |
Red | 2024-03-21 20:37:59 | 192 | Region A |
Blue | 2024-03-21 20:37:59 | 254 | Region A |
Green | 2024-03-21 20:37:59 | 215 | Region A |
Red | 2024-03-21 20:38:00 | 202 | Region A |
Blue | 2024-03-21 20:38:00 | 254 | Region A |
Green | 2024-03-21 20:38:00 | 207 | Region A |
Yellow | 2024-03-21 20:38:00 | 17 | Region A |
Red | 2024-03-21 20:38:01 | 189 | Region A |
Blue | 2024-03-21 20:38:01 | 254 | Region A |
Green | 2024-03-21 20:38:01 | 225 | Region A |
Yellow | 2024-03-21 20:38:01 | 16 | Region A |
Red | 2024-03-21 20:38:02 | 204 | Region A |
Blue | 2024-03-21 20:38:02 | 255 | Region A |
Green | 2024-03-21 20:38:02 | 213 | Region A |
Yellow | 2024-03-21 20:38:02 | 18 | Region A |
Red | 2024-03-21 20:38:03 | 180 | Region A |
Blue | 2024-03-21 20:38:03 | 254 | Region A |
Green | 2024-03-21 20:38:03 | 214 | Region A |
Yellow | 2024-03-21 20:38:03 | 13 | Region A |
Red | 2024-03-21 20:38:04 | 182 | Region A |
Blue | 2024-03-21 20:38:04 | 254 | Region A |
Green | 2024-03-21 20:38:04 | 213 | Region A |
Yellow | 2024-03-21 20:38:04 | 12 | Region A |
下面是我开始使用的Python代码。它仍然是逐行查看数据。我也从未使用过scikit-learn或Python,所以我对下面的内容并不自信:
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
data = pd.read_csv("data/combined.csv", header=0)
label_encoder = LabelEncoder()
print(data.columns)
data["name_encoded"] = label_encoder.fit_transform(data["name"])
data["location_encoded"] = label_encoder.fit_transform(data["location"])
x = data[["rssi", "name_encoded"]] # Features (rssi and encoded name)
y = data["location_encoded"] # Target (encoded location)
# Split data into training and testing sets
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)
# Create the Random Forest model
model = RandomForestClassifier(n_estimators=100)
# Train the model
model.fit(x_train, y_train)
# Make predictions on the testing set
y_pred = model.predict(x_test)
# Decode predictions
location_decoder = LabelEncoder()
location_decoder.fit(data["location"]) # Fit the decoder with original locations
predicted_locations = location_decoder.inverse_transform(y_pred)
print("Predicted locations:", predicted_locations)
非常感谢任何帮助。
1 个回答
我觉得把你的数据集从长格式转换成宽格式会更好。这样每一行就会有以下几个列:"时间"
、"红色"
、"黄色"
、"绿色"
、"蓝色"
、"地点"
。记住,输入到模型中的数据(或者说数据集中的一行)应该包含你想要的所有特征,所以可以把它们想象成你的列。
我通过以下方式实现了这个转换
# long to wide pivot
data = pd.pivot(df, index=["Time", "Location"], columns = "Receiver", values="RSSI")
# drop "Time" into a column form the index
data.reset_index(drop=False, inplace=True)
# replace NaN with 0
data.fillna(0, inplace = True)
# order columns to have location last
data = data[["Time", *df.Receiver.unique(), "Location"]]
在这里,我把你上面分享的表格定义成了一个名为 df
的 pandas 数据框。
这样我们就得到了
时间 | 地点 | 蓝色 | 绿色 | 红色 | 黄色 |
---|---|---|---|---|---|
2024-03-21 20:37:58 | 区域 A | 254.0 | 208.0 | 182.0 | 0.0 |
2024-03-21 20:37:59 | 区域 A | 254.0 | 215.0 | 192.0 | 0.0 |
2024-03-21 20:38:00 | 区域 A | 254.0 | 207.0 | 202.0 | 17.0 |
2024-03-21 20:38:01 | 区域 A | 254.0 | 225.0 | 189.0 | 16.0 |
2024-03-21 20:38:02 | 区域 A | 255.0 | 213.0 | 204.0 | 18.0 |
另外,为了对你的训练代码做一些小调整,通常做法是把数据集的最后一列作为标签或目标列,所以你可以简单地切片数据框,如下所示
x = data.iloc[:,:-1]
y = data.iloc[:,-1]
此外,LabelEncoder
应该只用于目标列,你在 "name"
上使用它是不正确的。通常你应该使用 OrdinalEncoder
来处理那种情况。现在我们不再需要这个,因为名字现在已经变成了独立的列。你还重新定义了标签编码器来解码输出,但你已经用标签编码器编码了目标,所以可以直接重用它。
你还需要把时间列转换成数字,任何方法都可以,所以可以尝试直接转换为整数。要注意这个数字会很大,而机器学习模型通常在值在 0 和 1 之间时表现更好,所以可以考虑使用一个 标准化器。
这是我对你的训练做的小更新
le = LabelEncoder()
y = le.fit_transform(y)
x['Time'] = pd.to_datetime(x['Time'])
x["Time"] = x["Time"].apply(lambda x: x.toordinal())
x = np.array(x)
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)
# Create the Random Forest model
model = RandomForestClassifier(n_estimators=100)
# Train the model
model.fit(x_train, y_train)
# Make predictions on the testing set
y_pred = model.predict(x_test)
predicted_locations = le.inverse_transform(y_pred)
print("Predicted locations:", predicted_locations)