#python #pandas #for-loop
Вопрос:
У меня есть набор данных ( df
), подобный этому
Name1 Name2 Score
John NaN NaN
Patty NaN NaN
где Name2
и Score
инициализируются NaN
. Некоторые данные, такие как следующие
name2_list=[[Chris, Luke, Martin], [Martin]]
score_list=[[1,2,4],[3],[]]
генерируется в каждом цикле из функции. Эти два списка необходимо добавить в столбцы Name2
и Score
в мой df
, чтобы иметь:
Name1 Name2 Score
John [Chris, Luke, Martin] [1,2,4]
Patty [Martin] [3]
Затем, поскольку я хочу иметь значения , а не списки в Name2
и Score
, я расширяю набор данных:
Name1 Name2 Name3
John Chris 1
John Luke 2
John Martin 4
Patty Martin 3
Моя цель-иметь все ценности Name2
внутри Name1
. Однако, как я уже упоминал, у меня есть функция , которая работает следующим образом: для каждого элемента in Name2
, а не in Name1
, она проверяет, есть ли дополнительные значения. Эти сгенерированные значения аналогичны тем, которые наблюдались для name2_list
и score_list
.
Например, предположим, что на второй итерации Chris
значения, сгенерированные из функции, равны [Patty]
и 9
; Luke
имеют значения [Martin]
и 1
; Martin
имеют значения [Laura]
и 3
. Затем мне нужно снова добавить эти значения к моему оригиналу df
, чтобы иметь (перед взрывом)
Name1 Name2 Score
John Chris 1
John Luke 2
John Martin 4
Patty Martin 3
Chris Patty 9
Luke Martin 1
Martin Laura 3
Только одно значение, Laura
, еще не Name1
введено, поэтому мне нужно будет снова запустить функцию: если вывод уже включен Name1
, то мой цикл остановится, и я получу окончательный набор данных; в противном случае мне нужно будет перезапустить функцию и посмотреть, не требуется ли больше циклов.
Чтобы сделать его короче в этом примере, давайте предположим, что значение Laura
после запуска функции равно John
, 3
. John
уже Name1
включен, поэтому мне не нужно повторно запускать функцию.
What I have done is the following:
name2_list, score_list = [],[] # Initialize lists. These two lists need to store outputs from my function
name2 = df['name2'] # Append new name2 to this list as I iterate
name1 = df['name1'] # Append new name1 to this list as I iterate
distinct_name1 = set(name1) # distinct name1. I need this to calculate the difference
diff = set(name2) ^ distinct_name1 # This calculates the difference. I need to iterate until this list is empty, i.e., when len(diff)=0
if df.Name2.isnull().all(): # this condition is to start the process. At the beginning I have only values in Name1. No values in Name2
if len(diff)>0: # in the example the difference is 2 at the beginning, i.e., John and Patty; at the second round 3 (Chris, Luke, Martin); at the third round is only for Laura. There is no fourth round
for x in diff: # I run it first for John, then for Patty
collected_data = fun(df, diff) # I will explain below what this function does and how it looks like
df = df.apply(pd.Series.explode) # in this step I explode the dataset
name2 = df['Name2'] # I am updating the list of values in Name2 to calculate the difference after each iteration.
name1 = df['Name1'] # I am updating the list of values in Name1 to calculate the difference after each iteration.
distinct_name1 = set(name1) # calculate the new difference
diff = filter(None, (set(name2) ^ distinct_name1) ) # calculate the new difference. Iterate until this is empty
Ошибка возникает, когда я рассматриваю этот шаг df['Name2'] = name2_list
в функции:
—> 33 df[‘Name2’] = список имен2_
поговорка:
Ошибка значения: Длина значений (6) не совпадает с длиной индекса (8).
(значения внутри круглых скобок могут отличаться от тех, которые вы могли бы получить, используя этот пример)
В настоящее время моей функции все равно, сколько строк находится во фрейме данных, и она создает новые списки некоторой разной длины. Мне нужно было бы найти способ примирить это. Я занимался отладкой и могу подтвердить, что ошибка возникла df['Name2'] = name2_list
в функции. Я могу правильно распечатать список новых значений name2, но не столбец. Возможно, возможным решением могло бы быть создание df один раз вне for
цикла, но мне нужно развернуть df['Name2']
и создать списки, в которых будут храниться результаты из Интернета.
Комментарии:
1. Что такое
check
переменная ? Основываясь на вашей логикеfun
, вам нужно убедиться, чтоassert len(df) == len(diff)
Ответ №1:
Я думаю, что это не очень хорошая идея использовать панд для решения такого рода проблем. Если вас устраивает простой python для промежуточных шагов, вы могли бы сделать это:
import pandas as pd
def get_links(source_name):
"""Dummy function with data from OP.
Note that it processes one name at a time instead of batch like in OP.
"""
dummy_output = {
'John': (
['Chris', 'Luke', 'Martin'],
[1, 2, 4]
),
'Patty': (
['Martin'],
[9]
),
'Chris': (
['Patty'],
[9]
),
'Luke': (
['Martin'],
[1]
),
'Martin': (
['Laura'],
[3]
),
'Laura': (
['John'],
[3]
)
}
target_names, scores = dummy_output.get(source_name, ([], []))
return [
{'name1': source_name, 'name2': target_name, 'score': score}
for target_name, score in zip(target_names, scores)
]
todo = ['John', 'Patty']
seen = set(todo)
data = []
while todo:
source_name = todo.pop(0) # If you don't care about order can .pop() to get last element (more efficient)
# get new data
new_data = get_links(source_name)
data = new_data
# add new names to queue if we haven't seen them before
new_names = set([row['name2'] for row in new_data]).difference(seen)
seen.update(new_names)
todo = list(new_names)
pd.DataFrame(data)
Выход:
name1 name2 score
0 John Chris 1
1 John Luke 2
2 John Martin 4
3 Patty Martin 9
4 Chris Patty 9
5 Luke Martin 1
6 Martin Laura 3
7 Laura John 3