【问题标题】:for loop using iterrows in pandas在 pandas 中使用 iterrows 的 for 循环
【发布时间】:2016-12-24 19:27:27
【问题描述】:

我有 2 个数据框如下:

data1 如下所示:

id          address       
1          11123451
2          78947591

data2 如下所示:

lowerbound_address   upperbound_address    place
78392888                 89000000            X
10000000                 20000000            Y

我想在 data1 中创建另一个名为“place”的列,其中包含 id 的来源。 例如,在上述情况下, 对于 id 1,我希望 place 列包含 Y,对于 id 2,我希望 place 列包含 X。 会有很多 id 来自同一个地方。有些 id 没有匹配项。

我正在尝试使用以下代码来执行此操作。

places = []
    for index, row in data1.iterrows():
        for idx, r in data2.iterrows():
            if r['lowerbound_address'] <= row['address'] <= r['upperbound_address']:
                places.append(r['place'])

这里的地址是浮点值。

运行这段代码需要很长时间。这让我想知道我的代码是否正确,或者是否有更快的执行方式。

任何帮助将不胜感激。 谢谢!

【问题讨论】:

  • 所以 data2 定义了从地址范围到地点的映射,对吗? data1["address"] 是熊猫系列。所以也许你可以使用map(或者如果你想处理数据框,也可以申请)。但是,可能有更快的方法,但我认为这应该已经提高了这里的速度
  • @Quickbeam2k1:是的,对于data1中的每个id,我想确定用户来自的地方。 Data2 包含每个地方的地址的下限和上限,我必须通过检查 id 的地址是否位于 data2 中给定的两个边界之间,将每个 id 从 data1 映射到 data2 中的一个地方。
  • 某个地址会有多个匹配项还是根本没有匹配项?当前代码无法正确处理。
  • 会有很多 id 来自同一个地方。有些 id 不匹配。
  • 两个数据框中有多少行?数据是来自数据库(例如 SQL Server、Postgres)还是您可以访问诸如文件级 SQLite 之类的数据库?对于区间合并,请考虑交叉连接过滤器,但在 SQL where 过滤器用于WHERE 子句(隐式连接和通常的第一个操作顺序命令)。

标签: python pandas


【解决方案1】:

您可以先将crossmerge 结合使用,然后按boolean indexing 过滤值。最后通过drop 删除不必要的列:

data1['tmp'] = 1
data2['tmp'] = 1
df = pd.merge(data1, data2, on='tmp', how='outer')
df = df[(df.lowerbound_address <= df.address) & (df.upperbound_address >= df.address)]
df = df.drop(['lowerbound_address','upperbound_address', 'tmp'], axis=1)
print (df)
   id   address place
1   1  11123451     Y
2   2  78947591     X

另一个解决方案是itertuples,最后创建DataFrame.from_records

places = []
for row1 in data1.itertuples():
    for row2 in data2.itertuples():
        #print (row1.address)
        if (row2.lowerbound_address <= row1.address <= row2.upperbound_address):
            places.append((row1.id, row1.address, row2.place))    
print (places)
[(1, 11123451, 'Y'), (2, 78947591, 'X')]

df = pd.DataFrame.from_records(places)
df.columns=['id','address','place']
print (df)
   id   address place
0   1  11123451     Y
1   2  78947591     X

apply 的另一个解决方案:

def f(x):
    for row2 in data2.itertuples():
        if (row2.lowerbound_address <= x <= row2.upperbound_address):
            return pd.Series([x, row2.place], index=['address','place'])

df = data1.set_index('id')['address'].apply(f).reset_index()
print (df)
   id   address place
0   1  11123451     Y
1   2  78947591     X

编辑:

时间安排

N = 1000:

如果saome 值不在范围内,解决方案中的bc 将被忽略。检查df1的最后一行。

In [73]: %timeit (data1.set_index('id')['address'].apply(f).reset_index())
1 loop, best of 3: 2.06 s per loop

In [74]: %timeit (a(df1a, df2a))
1 loop, best of 3: 82.2 ms per loop

In [75]: %timeit (b(df1b, df2b))
1 loop, best of 3: 3.17 s per loop

In [76]: %timeit (c(df1c, df2c))
100 loops, best of 3: 2.71 ms per loop

计时码

np.random.seed(123)
N = 1000
data1 = pd.DataFrame({'id':np.arange(1,N+1), 
                   'address': np.random.randint(N*10, size=N)}, columns=['id','address'])

#add last row with value out of range
data1.loc[data1.index[-1]+1, ['id','address']] = [data1.index[-1]+1, -1]
data1 = data1.astype(int)
print (data1.tail())

data2 = pd.DataFrame({'lowerbound_address':np.arange(1, N*10,10), 
                      'upperbound_address':np.arange(10,N*10+10, 10),
                      'place': np.random.randint(40, size=N)})

print (data2.tail())
df1a, df1b, df1c = data1.copy(),data1.copy(),data1.copy()
df2a, df2b ,df2c = data2.copy(),data2.copy(),data2.copy()

def a(data1, data2):
    data1['tmp'] = 1
    data2['tmp'] = 1
    df = pd.merge(data1, data2, on='tmp', how='outer')
    df = df[(df.lowerbound_address <= df.address) & (df.upperbound_address >= df.address)]
    df = df.drop(['lowerbound_address','upperbound_address', 'tmp'], axis=1)
    return (df)

def b(data1, data2):
    places = []
    for row1 in data1.itertuples():
        for row2 in data2.itertuples():
            #print (row1.address)
            if (row2.lowerbound_address <= row1.address <= row2.upperbound_address):
                places.append((row1.id, row1.address, row2.place))    

        df = pd.DataFrame.from_records(places)
        df.columns=['id','address','place']

    return (df)

def f(x):
    #use for ... else for add NaN to values out of range
    #http://stackoverflow.com/q/9979970/2901002
    for row2 in data2.itertuples():
        if (row2.lowerbound_address <= x <= row2.upperbound_address):
             return pd.Series([x, row2.place], index=['address','place'])
    else:
        return pd.Series([x, np.nan], index=['address','place'])

def c(data1,data2):
    data1 = data1.sort_values('address')
    data2 = data2.sort_values('lowerbound_address')
    df = pd.merge_asof(data1, data2, left_on='address', right_on='lowerbound_address')
    df = df.drop(['lowerbound_address','upperbound_address'], axis=1)
    return df.sort_values('id')


print (data1.set_index('id')['address'].apply(f).reset_index())
print (a(df1a, df2a))
print (b(df1b, df2b))
print (c(df1c, df2c))

只有cmerge_asof 的解决方案对大DataFrame 非常有效:

N=1M:

In [84]: %timeit (c(df1c, df2c))
1 loop, best of 3: 525 ms per loop

更多关于merge asof in docs

【讨论】:

  • 非常感谢!我不着急!!
  • 不幸的是你需要循环解决方案,itertuplesiterrows 好一点。但是,如果数据帧很大,不幸的是,所有解决方案都很慢。
  • 我认为第一次是正确的,但我不确定追加到列表是否只是必要的 - 也许数据可以移动,所以也需要提供列表 ID 和地址。但我认为申请解决方案可以更快,我可以创建一些时间。
  • 哦,是的,有道理。我现在正在尝试应用解决方案!感谢您提供所有三个解决方案!
  • apply 方法抛出错误:TypeError: 'NoneType' 类型的对象没有 len()。你知道为什么吗? :|
猜你喜欢
  • 2020-08-25
  • 2018-11-28
  • 2019-12-21
  • 2016-08-28
  • 1970-01-01
  • 2018-10-15
  • 1970-01-01
  • 2021-06-22
相关资源
最近更新 更多