pandas 如何避免遍历 Dataframe 行

lymgl2op  于 2023-03-21  发布在  其他
关注(0)|答案(2)|浏览(164)

我有一个dataframe df,其中包含'Name','Date'和'Id'列。'Id'列最初全为0,我想如下填充它:具有相同“Date”和same_names(name_i, name_j) == True的行采用相同的ID。
我设法用一个for循环来完成它,它遍历df的行:

from collections import defaultdict
import pandas as pd

def same_names(name1, name2):
    name1_parts = name1.split()
    name2_parts = name2.split()
    # Compare last names
    return name1_parts[-1] == name2_parts[-1]

# Sample data
data = [
    ['John Smith', '2022-01-01', 0],
    ['Mary Johnson', '2022-01-04', 0],
    ['Mark Williams', '2022-01-02', 0],
    ['Jessica Brown', '2022-01-03', 0],
    ['David Lee', '2022-01-03', 0],
    ['John Brown', '2022-01-02', 0],
    ['Frank Johnson', '2022-01-04', 0],
    ['Mary Lee', '2022-01-03', 0],
    ['David Lee', '2022-01-03', 0]
]

header = ['Name', 'Date', 'Id']

df = pd.DataFrame(data, columns=header)

date_to_index = defaultdict(list)
for index, row in df.iterrows():
    date = row['Date']
    if date in date_to_index:
        for i in date_to_index[date]:
            prev_row = df.iloc[i]
            if same_names(prev_row['Name'], row['Name']):
                df.at[index, 'Id'] = prev_row['Id']
            else:
                df.at[index, 'Id'] = df["Id"].max() + 1
                date_to_index[date].append(index)
    else:
        if index > 0:
            df.at[index, 'Id'] = df["Id"].max() + 1
        date_to_index[date].append(index)

df.sort_values(by="Id", inplace=True, ignore_index=True)
print(df)

结果:

Name        Date  Id
0     John Smith  2022-01-01   0
1   Mary Johnson  2022-01-04   1
2  Frank Johnson  2022-01-04   1
3  Mark Williams  2022-01-02   2
4  Jessica Brown  2022-01-03   3
5      David Lee  2022-01-03   4
6       Mary Lee  2022-01-03   4
7      David Lee  2022-01-03   4
8     John Brown  2022-01-02   5

有没有办法将这段代码向量化(或者用其他方法使它更快)?也许可以用groupby,但问题是我使用函数same_names来比较名称,而不仅仅是通过相等来比较,这会使它变得复杂。

注意same_names()函数可能与示例不同,因为实际上名称可能会更混乱(例如,不是'玛丽Json',而是'Johnson Mary'或'Ms Mary Johnson',所以我仍然需要弄清楚same_names()将是什么)。

qco9c6ql

qco9c6ql1#

您可以拆分列并获取list中的最后一个元素,然后使用pd.factorize将Series值编码为枚举类型

df['Id'] = pd.factorize(df['Name'].str.split(' ').str[-1])[0]
print(df)

            Name        Date  Id
0     John Smith  2022-01-01   0
1   Mary Johnson  2022-01-04   1
2  Mark Williams  2022-01-02   2
3  Jessica Brown  2022-01-03   3
4      David Lee  2022-01-03   4
5     John Brown  2022-01-02   3
6  Frank Johnson  2022-01-04   1
7       Mary Lee  2022-01-03   4
8      David Lee  2022-01-03   4
yfwxisqw

yfwxisqw2#

我知道你的函数same_names可能不同。
不过,如果总是比较,你可以使用一个函数(下面的identity)来计算一个用于分组的临时列。这与标准sorted()函数的key参数是相同的机制。
您还需要一个来自itertools的计数器来生成id。
如果不使用"Id"列,可以首先删除它。
下面是如何做到这一点:

>>> from itertools import count
>>> counter = count(0)

>>> def identity(s):
...     return s.split()[-1]

>>> df.assign(
...     identity=df["Name"].apply(identity),
... ).groupby(
...     by=["identity", "Date"], group_keys=False
... ).apply(
...     lambda x: x.assign(Id=next(counter)),
... ).drop(
...     columns=["identity"],
... ).sort_values(
...     by="Id", ignore_index=True
... )

            Name        Date  Id
0     John Brown  2022-01-02   0
1  Jessica Brown  2022-01-03   1
2   Mary Johnson  2022-01-04   2
3  Frank Johnson  2022-01-04   2
4      David Lee  2022-01-03   3
5       Mary Lee  2022-01-03   3
6      David Lee  2022-01-03   3
7     John Smith  2022-01-01   4
8  Mark Williams  2022-01-02   5

相关问题