使用 Panda 在 Python 中根据名称查找值之间的关系
Posted
技术标签:
【中文标题】使用 Panda 在 Python 中根据名称查找值之间的关系【英文标题】:Finding relationships between values based on their name in Python with Panda 【发布时间】:2021-03-10 19:19:40 【问题描述】:我想根据以下规则通过名称建立值之间的关系:
1- 我有一个包含很多值的 CSV 文件(超过 100000 行),我分享了一些示例如下:
Name:
A02-father
A03-father
A04-father
A05-father
A07-father
A08-father
A09-father
A17-father
A18-father
A20-father
A02-SA-A03-SA
A02-SA-A04-SA
A03-SA-A02-SA
A03-SA-A05-SA
A03-SA-A17-SA
A04-SA-A02-SA
A04-SA-A09-SA
A05-SA-A03-SA
A09-SA-A04-SA
A09-SA-A20-SA
A17-SA-A03-SA
A17-SA-A18-SA
A18-SA-A17-SA
A20-SA-A09-SA
A05-NA
B02-Father
B04-Father
B06-Father
B02-SA-B04-SA
B04-SA-BO2-SA
B04-SA-B06-SA
B06-SA-B04-SA
B06-NA
2- 现在我有另一个 CSV 文件,它让我知道我应该从哪个值开始?在这种情况下,值为 A03-father & B02-father & ... 相互之间没有任何影响,而且它们都有各自的路要走,所以对于每条路,我们都会从上面提到的起点开始。 父亲.csv A03-父亲 B02-父亲 ....
3-根据我要建立关系的命名,由于A03-父亲已被确定为父亲,我应该检查以A03开头的任何值。(它们都是A0的婴儿。) 同样,由于 B02 是父亲,我们将检查以 B02 开头的任何值。 (B02-SA-B04-SA)
4- 现在如果我发现 A03-SA-A02-SA ,这是 A03 的宝贝。 我发现 A03-SA-A05-SA ,这是 A03 的宝贝。 我发现 A03-SA-A17-SA ,这是 A03 的宝贝。
然后我必须检查任何以 A02 & A05 & A17 开头的节点: 如您所见,A02-Father 存在所以它是父亲,现在我们将搜索任何以 A02 开头并且没有被检测为父亲的 A03 的字符串(必须忽略)
这必须检查直到 CSV 文件中存在的值结束。 如您所见,我应该根据名称(REGEX)检查路径,并且应该继续前进直到路径结束。
预期结果:
Father Baby
A03-father A03-SA-A02-SA
A03-father A03-SA-A05-SA
A03-father A03-SA-A17-SA
A02-father A02-SA-A04-SA
A05-father A05-NA
A17-father A17-SA-A18-SA
A04-father A04-SA-A09-SA
A02-father A02-SA-A04-SA
A09-father A09-SA-A20-SA
B02-father B02-SA-B04-SA
B04-father B04-SA-B06-SA
B06-father B06-NA
我用 pandas 将其编码如下:
import pandas as pd
import numpy as np
import re
#Read the file which consists of all Values
df = pd.read_csv("C:\\total.csv")
#Read the file which let me know who is father
Fa = pd.read_csv("C:\\Father.csv")
#Get the first part of Father which is A0
Fa['sub'] = Fa['Name'].str.extract(r'(\w+\s*)', expand=False)
r2 = []
#check in all the csv file and find anything which starts with A0 and is not Father
for f in Fa['sub']:
baby=(df[df['Name'].str.startswith(f) & ~df['Name'].str.contains('Father')])
baby['sub'] = bay['Name'].str.extract(r'(\w+\s*)', expand=False)
r1= pd.merge(Fa, baby, left_on='sub', right_on='sub',suffixes=('_f', '_c'))
r2.append(result1)
out_df = pd.concat(result2)
out_df= out_df.replace(np.nan, '', regex=True)
#find A0-N-A2-M and A0-N-A4-M
out_df.to_csv('C:\\child1.csv')
#check in all the csv file and find anything which starts with the second part of child1 which is A2 and A4
out_df["baby2"] = out_df['Name_baby'].str.extract(r'^(?:[^-]*-)2\s*([^-]+)', expand=False)
baby3= out_df["baby2"]
r4 = []
for f in out_df["baby2"]:
#I want to exclude A0 which has been detected.
l = ['A0']
regstr = '|'.join(l)
baby1=(df[df['Name'].str.startswith(f) & ~df['Name'].str.contains(regstr)])
baby1['sub'] = baby1['Name'].str.extract(r'(\w+\s*)', expand=False)
r3= pd.merge(baby3, baby1, left_on='baby2', right_on='sub',suffixes=('_f', '_c'))
r4.append(r3)
out2_df = pd.concat(r4)
out2_df.to_csv('C:\\child2.csv')
我想将下面的代码放在一个循环中,并根据命名过程检查文件并检查其他父亲和婴儿,直到完成。但是,此代码不是自定义的,并且没有我预期的确切结果。 我的问题是关于如何制作循环?
我应该通过路径并考虑任何字符串的regstr
值。
#check in all the csv file and find anything which starts with the second part of child1 which is A2 and A4
out_df["baby2"] = out_df['Name_baby'].str.extract(r'^(?:[^-]*-)2\s*([^-]+)', expand=False)
baby3= out_df["baby2"]
r4 = []
for f in out_df["baby2"]:
#I want to exclude A0 which has been detected.
l = ['A0']
regstr = '|'.join(l)
baby1=(df[df['Name'].str.startswith(f) & ~df['Name'].str.contains(regstr)])
baby1['sub'] = baby1['Name'].str.extract(r'(\w+\s*)', expand=False)
r3= pd.merge(baby3, baby1, left_on='baby2', right_on='sub',suffixes=('_f', '_c'))
r4.append(r3)
out2_df = pd.concat(r4)
out2_df.to_csv('C:\\child2.csv')
【问题讨论】:
您说Now I have another CSV file which let me know from which value I should start? in this case the value
,但您也可以处理数据以获得您想要的结果,而无需获取开始的值。确实如此,这将非常简单,并且只需很短的代码即可完成 - 如果是,请确认。
我没有明白这一点,但你应该有一个路径的起点。根据起点,您可以找到前进的方向。这是非常重要的原因,因为您看到 A0-N-A2-M 和 A2-M-A0-N 都存在,当您从 A0 开始时,您接受 A0-N-A2-M 是婴儿。但是当你作为父亲去 A2 时,你不接受 A2-M-A0-N 作为孩子。由于A0一直是父亲。由于这个原因,我要求从一条路径开始。这正是我正在处理的问题。
这是什么意思:A05-NA?
【参考方案1】:
以import collections
开头(很快就会需要)。
我假设你已经阅读了 df 和 Fa DataFrames。
我的代码的第一部分是创建 children 系列(索引 - 父级, 价值 - 孩子):
isFather = df.Name.str.contains('-father', case=False)
dfChildren = df[~isFather]
key = []; val = []
for fath in df[isFather].Name:
prefix = fath.split('-')[0]
for child in dfChildren[dfChildren.Name.str.startswith(prefix)].Name:
key.append(prefix)
val.append(child)
children = pd.Series(val, index=key)
打印 children 以查看结果。
第二部分是创建实际结果,从每个 Fa中的起点:
nodes = collections.deque()
father = []; baby = [] # Containers for source data
# Loop for each starting point
for startNode in Fa.Name.str.split('-', expand=True)[0]:
nodes.append(startNode)
while nodes:
node = nodes.popleft() # Take node name from the queue
# Children of this node
myChildren = children[children.index == node]
# Process children (ind - father, val - child)
for ind, val in myChildren.items():
parts = val.split('-') # Parts of child name
# Child "actual" name (if exists)
val_2 = parts[2] if len(parts) >= 3 else ''
if val_2 not in father: # val_2 not "visited" before
# Add father / child name to containers
father.append(ind)
baby.append(val)
if len(val_2) > 0:
nodes.append(val_2) # Add to the queue, to be processe later
# Drop rows for "node" from "children" (if any exists)
if (children.index == node).sum() > 0:
children.drop(node, inplace=True)
# Convert to a DataFrame
result = pd.DataFrame('Father': father, 'Baby': baby)
result.Father += '-father' # Add "-father" to "bare" names
我添加了-father 小写“f”,但我认为这并不多 重要的细节。
您的数据样本的结果是:
Father Baby
0 A03-father A03-SA-A02-SA
1 A03-father A03-SA-A05-SA
2 A03-father A03-SA-A17-SA
3 A02-father A02-SA-A04-SA
4 A05-father A05-NA
5 A17-father A17-SA-A18-SA
6 A04-father A04-SA-A09-SA
7 A09-father A09-SA-A20-SA
8 B02-father B02-SA-B04-SA
9 B04-father B04-SA-B06-SA
10 B06-father B06-NA
关于您的数据样本的两个评论:
你写的 B04-SA-B02-SA 用大写 O(一个字母)而不是 0 (零)。我在源数据中更正了它。 预期结果中的A02-father A02-SA-A04-SA
行加倍。
我认为它应该只发生一次。
【讨论】:
这对于Father.csv中的一条记录非常有效,我在Father.csv中有大约200条记录,代码只是检查第一个父亲。 您的输入数据样本 (Fa) 仅包含一行。当它包含更多行时,请说明您的期望。第一个假设可以是为每个起点运行我的代码并连接结果。但也许之前发现的(对于以前的起点)应该对当前周期的运作方式产生一些影响?提供一个 Fa 的例子,例如2个起点,预期结果和一些解释。 希望他们不会对彼此产生任何影响。我更新了我的例子和必要的解释。我认为 Fa.iloc[0,0] 必须更改并包含fathers.csv中存在的所有值 这很好用。我不知道该怎么说谢谢。如果您只是在代码中添加注释,我真的很感激。所以我可以更好地理解它。谢谢 我添加了一些 cmets。您还可以添加一些“跟踪”打印输出并在一些有限的源数据上运行代码。【参考方案2】:内嵌注释
def find(data, from_pos=0):
fathers =
skip = []
for x in data[from_pos:]:
tks = x.split("-")
# Is it father ?
if tks[1].lower() == "father":
fathers[tks[0]] = x
else:
if tks[0] in fathers and tks[-2] not in skip:
print (fathers[tks[0]], x)
# Skip this father appearing as child later
skip.append(tks[0])
测试用例:
data = [
'A0-Father',
'A0-N-A2-M',
'A0-N-A4-M',
'A2-Father',
'A2-M-A0-N',
'A2-N-A8-M',
'A8-father',
'A8-M-A11-N',
'A8-M-A2-N']
find(data, from_pos=0)
输出:
A0-Father A0-N-A2-M
A0-Father A0-N-A4-M
A2-Father A2-N-A8-M
A8-father A8-M-A11-N
编辑 1:
从一些数据开始测试
data = [
'A02-father',
'A03-father',
'A04-father',
'A05-father',
'A07-father',
'A08-father',
'A09-father',
'A17-father',
'A18-father',
'A20-father',
'A02-SA-A03-SA',
'A02-SA-A04-SA',
'A03-SA-A02-SA',
'A03-SA-A05-SA',
'A03-SA-A17-SA',
'A04-SA-A02-SA',
'A04-SA-A09-SA',
'A05-SA-A03-SA',
'A09-SA-A04-SA',
'A09-SA-A20-SA',
'A17-SA-A03-SA',
'A17-SA-A18-SA',
'A18-SA-A17-SA',
'A20-SA-A09-SA',
'A05-NA',
]
father = [
'A03-father',
]
首先让我们创建一个数据结构,这样操作会很容易,并且在您拥有大量数据时查找关系会很快
def make_data_structure(data):
all_fathers, all_relations = ,
for x in data:
tks = x.split("-")
if tks[1].lower() == "father":
all_fathers[tks[0]] = x
else:
if len(tks) == 2:
tks.extend(['NA', 'NA'])
if tks[0] in all_relations:
all_relations[tks[0]][0].append(tks[-2])
all_relations[tks[0]][1].append(x)
else:
all_relations[tks[0]] =[[tks[-2]], [x]]
return all_fathers, all_relations
all_fathers, all_relations = make_data_structure(data)
all_fathers, all_relations
输出:
'A02': 'A02-father',
'A03': 'A03-father',
'A04': 'A04-father',
'A05': 'A05-father',
'A07': 'A07-father',
'A08': 'A08-father',
'A09': 'A09-father',
'A17': 'A17-father',
'A18': 'A18-father',
'A20': 'A20-father',
'A02': [['A03', 'A04'], ['A02-SA-A03-SA', 'A02-SA-A04-SA']],
'A03': [['A02', 'A05', 'A17'],
['A03-SA-A02-SA', 'A03-SA-A05-SA', 'A03-SA-A17-SA']],
'A04': [['A02', 'A09'], ['A04-SA-A02-SA', 'A04-SA-A09-SA']],
'A05': [['A03', 'NA'], ['A05-SA-A03-SA', 'A05-NA']],
'A09': [['A04', 'A20'], ['A09-SA-A04-SA', 'A09-SA-A20-SA']],
'A17': [['A03', 'A18'], ['A17-SA-A03-SA', 'A17-SA-A18-SA']],
'A18': [['A17'], ['A18-SA-A17-SA']],
'A20': [['A09'], ['A20-SA-A09-SA']]
如您所见,all_fathers
包含所有父母,最重要的是 all_relations
包含父子关系,可以使用 father
进行索引以加快查找速度。
我们如何对关系进行实际解析
def find(all_fathers, all_relations, from_father):
fathers = [from_father]
skip = []
while True:
if len(fathers) == 0:
break
current_father = fathers[0]
fathers = fathers[1:]
for i in range(len(all_relations[current_father][0])):
if not all_relations[current_father][0][i] in skip:
print (all_fathers[current_father], all_relations[current_father][1][i])
if all_relations[current_father][0][i] != 'NA':
fathers.append(all_relations[current_father][0][i])
skip.append(current_father)
for x in father:
find(all_fathers, all_relations, x.split("-")[0])
输出:
A03-father A03-SA-A02-SA
A03-father A03-SA-A05-SA
A03-father A03-SA-A17-SA
A02-father A02-SA-A04-SA
A05-father A05-NA
A17-father A17-SA-A18-SA
A04-father A04-SA-A09-SA
A09-father A09-SA-A20-SA
编辑 2:
新的测试用例; [您必须将father.csv 中的值加载到名为father
的列表中。
data = [
'A02-father',
'A03-father',
'A04-father',
'A05-father',
'A07-father',
'A08-father',
'A09-father',
'A17-father',
'A18-father',
'A20-father',
'A02-SA-A03-SA',
'A02-SA-A04-SA',
'A03-SA-A02-SA',
'A03-SA-A05-SA',
'A03-SA-A17-SA',
'A04-SA-A02-SA',
'A04-SA-A09-SA',
'A05-SA-A03-SA',
'A09-SA-A04-SA',
'A09-SA-A20-SA',
'A17-SA-A03-SA',
'A17-SA-A18-SA',
'A18-SA-A17-SA',
'A20-SA-A09-SA',
'A05-NA',
'B02-Father',
'B04-Father',
'B06-Father',
'B02-SA-B04-SA',
'B04-SA-B02-SA',
'B04-SA-B06-SA',
'B06-SA-B04-SA',
'B06-NA',
]
father = [
'A03-father',
'B02-father'
]
for x in father:
find(all_fathers, all_relations, x.split("-")[0])
输出:
A03-father A03-SA-A02-SA
A03-father A03-SA-A05-SA
A03-father A03-SA-A17-SA
A02-father A02-SA-A04-SA
A05-father A05-NA
A17-father A17-SA-A18-SA
A04-father A04-SA-A09-SA
A09-father A09-SA-A20-SA
B02-Father B02-SA-B04-SA
B04-Father B04-SA-B06-SA
B06-Father B06-NA
【讨论】:
mujjiga,感谢您的代码,但任何包含父亲的节点都不是父亲。正如我所说,路径将从确定谁是父亲的 CSV 文件开始,我们必须按路径中的名称。您的代码非常适合该示例。但有 100000 行的错误。 我已经更新了我的示例,但在这个示例中代码无法正常工作。 @SaraDaniel 请检查我的编辑以回答 [编辑 1] 谢谢,当我在father.csv中只指定一个父亲时,这很好用,如果我有超过1个会发生什么...这只是示例。我的father.csv 文件中有200 多个父亲,希望他们彼此之间没有任何影响,并且他们有不同的路径。在这种情况下,我刚刚更新了我的示例。 它将适用于名为father
的列表中的所有父亲,因为我们循环它们您必须将father.csv 的值加载到名为father
的列表中。检查编辑 2以上是关于使用 Panda 在 Python 中根据名称查找值之间的关系的主要内容,如果未能解决你的问题,请参考以下文章
python 根据python字典快速查找排序ID(seqID)或加入名称。最初是为了处理来自100的加入