【问题标题】:How to combine two pandas dataframes with a conditional?如何将两个熊猫数据框与条件相结合?
【发布时间】:2016-12-02 03:17:53
【问题描述】:

我有两个 pandas 数据框,我想将它们与规则结合起来。

这是第一个数据帧

import pandas as pd
df1 = pd.Dataframe()

df1 

rank    begin    end     labels
first   30953   31131    label1
first   31293   31435    label2
first   31436   31733    label4
first   31734   31754    label1
first   32841   33037    label3
second  33048   33456    label4
....

第二个数据框只有两列,rankstart

df2

rank    start 
first   31333     
first   31434     
first   33039    
first   33123     
first   33125     

在第一个数据帧df1 中,数据有beginend。我想检查df2中start的整数是否在这个范围内。

这是它的最终结果:

result

rank    start     labels
first   31333     label2
first   31434     label2
first   33039     NaN
first   33123     label4
first   33125     label4

start==31333 介于df1label = label2 中的3129331435 之间。整数31434 也在31293:31435 范围内,所以它也被label2 注释。 33039 的值不在df2 的任何区间之间,所以它得到一个NaN 的值。

这些数据帧的组合规则是这样的:

(df2.start >= df1.begin) & (df2.start <= df1.end)

而且,每一行必须匹配相同的排名值,例如对于此条件,每一行都必须首先匹配字符串。

这是我用来组合这两个数据帧的代码,但它的扩展性并不好:

from numpy import nan

def between_range(row):
    subset = df1.loc[(row["rank"] == df1.rank) & (row.start >= repeats.start) & (row.start <= repeats.end), :]
    if subset.empty:
        return np.nan
    return subset.labels

是否有另一种方法可以通过合并(可能是排名)来做到这一点?还有其他基于 pandas 的解决方案吗?

【问题讨论】:

  • @Kartik 我试图在这里简化问题。感谢您的帮助
  • @MaxU 我已经更新了上面的内容。有什么想法可以扩展/工作吗?

标签: python pandas dataframe merge dask


【解决方案1】:

试试这个代码块

def match_labels(row):
    curr_df = df1[ (df1['rank']==row['rank']) & (df1['begin']<=row['start']) & (df1['end']>=row['start']) ]
    try:
        row['labels'] = curr_df['labels'].iloc[0]
    except:
        row['labels'] = np.NaN

    return row

result = df2.apply(lambda x:match_labels(x),axis=1)

希望对你有帮助

【讨论】:

  • 感谢您的回复。为什么当我上面的脚本运行了几天而没有显示时,这会起作用?
【解决方案2】:

如果您可以将 len(df1)*len(df2) 行数据放入内存,则可以通过大规模连接快速完成所有操作:

df = df2.merge(df1, how = 'left')
df = df.loc[(df.start >= df.begin) & (df.start <= df.end),['rank','start','labels']] # This gives you the correct label of every entry that does indeed belong to a label.
result = df2.merge(df, how = 'left') # This effectively adds the entries that do not belong to any label back into df.

此解决方案还处理start 位于多个beginend 对范围内的情况:在这种情况下,您将获得与匹配标签一样多的行。

如果您无法将其放入内存中,您可以尝试按 rank 对数据进行分区:仅对带有 rank == 'first' 的数据执行此操作,然后是 rank == 'second',依此类推。例如beginendstartdf = df2[(df2.start &gt; 31000) &amp; (df2.start &lt;= 32000)].merge(df1[(df1.begin &gt; 31000) &amp; (df1.end &lt;= 32000)], how = 'left')

【讨论】:

  • "如果您无法将其放入内存中,您可以尝试按排名对数据进行分区:仅对排名 == 'first',然后排名 == 'second' 的数据执行此操作,然后很快。”你可以说得更详细点吗?你会尝试df_rank1 = df.loc[(df.start &gt;= df.begin) &amp; (df.start &lt;= df.end) &amp; (rank=='first',['rank','start','labels']]result_rank1 = df2.merge(df, how = 'left')然后在最后将所有这些连接在一起吗?
  • 我已对我的答案进行了编辑。如果您看不到代码的工作原理,请尝试使用较小的数据子集运行它,并查看每一行的作用。
  • 当使用较小的子集时,即 `rank == 'first', etc., the RAM needed is too large. However, if I were to implement this, how could I use a for` 循环为每个“dataframe subsection”执行这些命令,然后将它们全部放在一起?
  • rank 使用较小的子集将无济于事(很多),因为我已经纠正了自己。您需要按避免连接的因素进行子集化,您知道这些连接将被过滤掉,在这种情况下,这将是 beginend 间隔。实现它的一种方法是将循环的每个输出收集到dfs = [df1,df2,...] 的列表中,然后使用pd.concat(dfs)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-01-24
  • 2018-09-16
  • 2017-12-27
  • 2018-05-28
  • 2021-06-18
  • 1970-01-01
  • 2017-06-11
相关资源
最近更新 更多