【问题标题】:Filtering non-'cohorts' from dataset从数据集中过滤非“群组”
【发布时间】:2021-01-18 07:09:52
【问题描述】:

我确信这个主题之前已经研究过,我不确定它叫什么或者我还应该研究什么技术,因此我在这里。我主要在 Python 和 Pandas 中运行它,但它不限于这些语言/技术。

举个例子,假设我有这个数据集:

| PID | A    | B    | C    |
| --- | ---- | ---- | ---- |
| 508 | 0.85 | 0.51 | 0.05 |
| 400 | 0.97 | 0.61 | 0.30 |
| 251 | 0.01 | 0.97 | 0.29 |
| 414 | 0.25 | 0.04 | 0.83 |
| 706 | 0.37 | 0.32 | 0.33 |
| 65  | 0.78 | 0.62 | 0.25 |
| 533 | 0.24 | 0.15 | 0.88 |

PID 是该行的唯一 ID。 A、B 和 C 是一些因素(在本例中已标准化)。这些数据集可以是历史上某个体育联盟的球员,也可以是库存中的产品,也可以是选民数据。具体的上下文并不重要。

现在假设我有一些输入数据:

| A    | B    | C    |
| ---- | ---- | ---- |
| 0.81 | 0.75 | 0.17 |

此输入与原始数据集(A、B、C)共享相同的因素。我想要做的是找到与我的输入数据(“群组”)相似的行。解决这个问题的最佳方法是什么?

我想到了聚类,使用kNN算法,但问题是队列的数量没有设置。您可能有独特的输入并且很少/没有“群组”,或者您可能有非常常见的输入并且有数百个“群组”。

我接下来尝试的解决方案是欧几里得距离。所以对于这个数据集和输入,我会做这样的事情:

my_cols = ['A', 'B', 'C']

inputdata = pd.Series([0.81, 0.75, 0.17], index=['A', 'B', 'C'])

# df = pandas data frame with above data

df['Dict'] = (df[my_cols] - inputdata).pow(2).sum(1).pow(0.5)

这将在数据集上创建一个新列,例如:

| PID | A    | B    | C    | Dist |
| --- | ---- | ---- | ---- | ---- |
| 508 | 0.85 | 0.51 | 0.05 | 0.27 |
| 400 | 0.97 | 0.61 | 0.30 | 0.25 |
| 251 | 0.01 | 0.97 | 0.29 | 0.84 |
| 414 | 0.25 | 0.04 | 0.83 | 1.12 |
| 706 | 0.37 | 0.32 | 0.33 | 0.63 |
| 65  | 0.78 | 0.62 | 0.25 | 0.16 |
| 533 | 0.24 | 0.15 | 0.88 | 1.09 |

然后您可以“过滤”掉低于某个阈值的那些行。

cohorts = df[df['Dist'] <= THRESHOLD]

然后问题变成(1)您如何确定最佳阈值? (2) 如果我在数据集和 Euclid 计算中添加第 4 个因子(“D”),它似乎会“破坏”结果,因为从结果来看,同类群组不再具有直观意义。

所以我的问题是:过滤/选择“群组”(类似于输入行的那些行)的技术或更好的方法是什么?

谢谢

【问题讨论】:

  • 你不能得到Dist最低的行吗?那将是最接近您的输入数据的吧?
  • 这将是“最相似”的同类群组,而不是“所有相似的”同类群组。
  • 这个问题很难以目前的形式回答,应该编辑以包含更多信息或关闭。最佳相似度指标完全取决于您想要实现的目标。如果你给我足够的例子,我可以尝试以最小的错误来设计一个过滤器,但如果没有那个或者你想要的相似性的明确定义,我担心什么都做不了。这取决于问题,并且没有普遍最优的相似性度量。

标签: python pandas statistics data-modeling


【解决方案1】:

这是我自己通过逻辑思维和一些基本统计数据得出的算法。它使用值的mean 和输入数据的平均值来根据standard deviation 使用pd.merge_asof 找到最接近的匹配项:

factors = ['A', 'B', 'C']
df = df.assign(avg=df[factors].mean(axis=1)).sort_values('avg')
input_data = input_data.assign(avg=input_data[factors].mean(axis=1)).sort_values('avg')

dfn = pd.merge_asof(
    df,
    input_data,
    on='avg',
    direction='nearest',
    tolerance=df['avg'].std()
)
   PID   A_x   B_x   C_x       avg   A_y   B_y   C_y
0  706  0.37  0.32  0.33  0.340000   NaN   NaN   NaN
1  414  0.25  0.04  0.83  0.373333   NaN   NaN   NaN
2  251  0.01  0.97  0.29  0.423333   NaN   NaN   NaN
3  533  0.24  0.15  0.88  0.423333   NaN   NaN   NaN
4  508  0.85  0.51  0.05  0.470000   NaN   NaN   NaN
5   65  0.78  0.62  0.25  0.550000  0.81  0.75  0.17
6  400  0.97  0.61  0.30  0.626667  0.81  0.75  0.17

【讨论】:

  • 我今天一直在想这个,谢谢你的回复。似乎这个解决方案是基于每行中因子的平均值,但它们不一定相关 - 这有关系吗?它似乎也只将那些输入行与df 行连接起来,其中输入行均值在df avg st.dev 的 1 st.dev 内 - 应该是另一种方式,还是有关系?跨度>
【解决方案2】:

您面临聚类问题,因此您的 K-means 直觉是正确的。

聚类

但是,正如您提到的,K-means 是一种参数化方法,因此您需要确定正确的 K。有一种自动方法可以找到关于集群质量(形状、稳定性、同质性)的最佳 K,称为肘部方法:https://www.scikit-yb.org/en/latest/api/cluster/elbow.html

然后,您可以使用另一种聚类方法(实际上正确的聚类算法取决于您的特征的含义),例如您可以使用基于密度的方法和 DBSCAN (https://scikit-learn.org/stable/modules/generated/sklearn.cluster.DBSCAN.html)。

因此,您需要根据您的问题确定最佳聚类算法:https://machinelearningmastery.com/clustering-algorithms-with-python/

使用此解决方案,您将在您的训练集(您将其命名为“同类”集)上拟合您的聚类算法,并使用该模型来预测您的“非同类”样本上的聚类。

统计群组

在某些领域,例如营销领域,您还可以找到基于一些数字属性和使用描述性统计数据创建集群(群组)的方法。

最好的例子是 RFM 分割方法,这是一种非常聪明的聚类方法,同时保持结果聚类的高清晰度:https://towardsdatascience.com/know-your-customers-with-rfm-9f88f09433bc

使用这种方法,您将在整个数据集上构建特征,然后根据特征值获取结果段。

【讨论】:

    【解决方案3】:

    由于您并不真正知道集群(群组)的数量或其结构,我相信OPTICS 算法最适合您。它找到一组打包在一起的点(使用欧几里得距离),并扩展以从它们构建一个集群。然后很容易找到新点所属(或不属于)的集群。它类似于DBSCAN,但不假设集群之间的密度相似。 sklearn 库包含 OPTICS 的实现。

    【讨论】:

      【解决方案4】:

      我的理解是,您希望独立考虑到每列的距离,但在最终结果中收集在一起。

      要获得独立核算,您可以使用其standard deviation σ (whimsical set of explanations) 来衡量列中成员的差异程度。

      要收集最终结果,您可以迭代过滤数据框,删除超出所需范围的行。这也连续减少了处理时间,尽管除非您有大量数据,否则它可以忽略不计。

      如果添加第四列导致没有数据足够接近,这可能表明

      • 您的测试数据确实与任何源数据都不接近,并且是唯一的条目
      • 您的数据不是正态分布的(如果有更多数据可用,您可以使用scikit.stats.normaltest 进行测试)
      • 您的列不是独立的(即需要更专业的统计处理)

      如果是第二个或第三个,则不应使用正态标准差,而应使用来自另一个分布的标准差(listmore tests

      但是,如果您的数据看似随机,您可以在每列中应用标准差的某些因子和/或幂(即方差)以获得或多或少准确的结果。


      初始数据帧

      starting data (df)
      
      PID       A     B     C
      508.0  0.85  0.51  0.05
      400.0  0.97  0.61   0.3
      251.0  0.01  0.97  0.29
      414.0  0.25  0.04  0.83
      706.0  0.37  0.32  0.33
      65.0   0.78  0.62  0.25
      533.0  0.24  0.15  0.88
      
      test data (test_data)
      
            A     B     C
      0  0.81  0.75  0.17
      

      df.std()

      找出每一列的标准差并将其收集到一个新的数据帧中

      然后用这个组装另一个数据框

      stdv = df.std()
      
      PID
      A    0.367145
      B    0.316965
      C    0.312219
      
      test_df = pd.DataFrame()
      test_df = test_df.append(test_data - stdv)
      test_df = test_df.append(test_data + stdv)
      test_df.index = ["low", "high"]
      
      test_df
      
                   A         B         C
      low   0.442855  0.433035 -0.142219
      high  1.177145  1.066965  0.482219
      

      结果

      遍历列,过滤掉所需范围之外的列(pandas Series.between() 可以为您做到这一点!)

      for x in df:
          df = df[df[x].between(test_df[x]["low"], test_df[x]["high"])]
      
      resulting df
      
      PID       A     B     C
      508.0  0.85  0.51  0.05
      400.0  0.97  0.61   0.3
      65.0   0.78  0.62  0.25
      

      【讨论】:

        猜你喜欢
        • 2012-05-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-02-12
        • 1970-01-01
        • 1970-01-01
        • 2023-01-09
        • 2011-11-28
        相关资源
        最近更新 更多