Как создать столбцы на основе комбинаций значений между двумя наборами столбцов? - PullRequest
0 голосов
/ 01 июля 2018

У меня есть фрейм данных со значениями, разделенными запятыми, которые я выделил с помощью pd.concat.

оригинал df:

org    country         type
Orange   USA, GBR, AUS   OWF, PMR, KIQ
Red      AUS, RUS, NZL   DOG, MOP, LOF

разделение столбцов дает мне df, который мы назовем df_wide,

org        country_1    country_2   country_3   type_1   type_2   type_3
Orange        USA          GBR         AUS         OWF      PMR      KIQ
Watermelon    AUS          RUS         NZL         ODG      MOP      LOF

Из приведенного выше кадра данных мне нужно получить все возможные комбинации одной страны и одного типа в длинном формате:

org     country    type
Orange  USA        OWF
Orange  USA        PMR
Orange  USA        KIQ
Orange  GBR        OWF
Orange  GBR        PMR
Orange  GBR        KIQ

.. и пр.

и вот где я застрял. Я ошибочно подумал, что могу просто преобразовать фрейм данных, используя pd.wide_to_long, но я думаю, что мой ответ вращается вокруг использования itertools. Я искал форумы, которые касаются этой проблемы, но я все еще не совсем понял это. Ищете любые предложения! Кроме того, значения, разделенные запятыми в исходных столбцах df, могут составлять десятки значений, и поэтому я не знаю, сколько в ширину столбцов будет иметь мой широкий df.

Ответы [ 3 ]

0 голосов
/ 01 июля 2018

Я начинаю с настройки df:

import pandas
records = [
    {
        "org": "Orange",
        "country_1": "USA",
        "country_2": "GBR",
        "country_3": "AUS",
        "type_1": "OWF",
        "type_2": "PMR",
        "type_3": "KIQ"
    },
    {
        "org": "Watermelon",
        "country_1": "AUS",
        "country_2": "RUS",
        "country_3": "NZL",
        "type_1": "ODG",
        "type_2": "MOP",
        "type_3": "LOF"
    }
]

df = pandas.DataFrame(records)

Прежде всего, вы можете использовать метод .filter из pandas.DataFrame, чтобы выбрать столбцы через регулярное выражение (как показано здесь ):

>>> df_countries = df.filter(regex=("country_.*"))
  country_1 country_2 country_3
0       USA       GBR       AUS
1       AUS       RUS       NZL

>>> df_types = df.filter(regex=("type_.*"))
  type_1 type_2 type_3
0    OWF    PMR    KIQ
1    ODG    MOP    LOF

Тогда вы можете получить все уникальные страны и типы как таковые:

>>> countries_all = df_countries.values.flatten()
array(['USA', 'GBR', 'AUS', 'AUS', 'RUS', 'NZL'], dtype=object)
>>> types_all = df_types.values.flatten()
array(['OWF', 'PMR', 'KIQ', 'ODG', 'MOP', 'LOF'], dtype=object)

их объединение - это вопрос использования декартового произведения из itertools:

>>> pandas.DataFrame(list(itertools.product(*[list(countries_all), list(types_all)])))
      0    1
0   USA  OWF
1   USA  PMR
2   USA  KIQ
3   USA  ODG
4   USA  MOP
5   USA  LOF
6   GBR  OWF
7   GBR  PMR
8   GBR  KIQ
9   GBR  ODG
10  GBR  MOP
11  GBR  LOF
12  AUS  OWF
13  AUS  PMR
14  AUS  KIQ
15  AUS  ODG
16  AUS  MOP
17  AUS  LOF
18  AUS  OWF
19  AUS  PMR
20  AUS  KIQ
21  AUS  ODG
22  AUS  MOP
23  AUS  LOF
24  RUS  OWF
25  RUS  PMR
26  RUS  KIQ
27  RUS  ODG
28  RUS  MOP
29  RUS  LOF
30  NZL  OWF
31  NZL  PMR
32  NZL  KIQ
33  NZL  ODG
34  NZL  MOP
35  NZL  LOF

Теперь я понимаю, что вы могли бы сделать это за org, и в этом случае я бы подставил подкадр данных до выполнения фильтра:

orgs = pandas.unique(df["org"])
for org in orgs:
    df_org = df[df["org"] == org]
    df_countries = df_org.filter(regex=("country_.*"))
    df_types = df_org.filter(regex=("type_.*"))
    # do rest of the process here and concatenate in the end through `pandas.concat`

Надеюсь, это поможет

0 голосов
/ 01 июля 2018

Просто позаимствуйте настройку jpp, используя pd.MultiIndex.from_product

df['country'] = df['country'].str.split(', ')
df['type'] = df['type'].str.split(', ')
s=[pd.MultiIndex.from_product(x).tolist() for x in list(zip(df['country'],df['type']))]

df=pd.DataFrame({'org':df.org.repeat(list(map(len,s)))}).reset_index(drop=True)

df[['country','type']]=pd.DataFrame(sum(s,[]))
df
       org country type
0   Orange     USA  OWF
1   Orange     USA  PMR
2   Orange     USA  KIQ
3   Orange     GBR  OWF
4   Orange     GBR  PMR
5   Orange     GBR  KIQ
6   Orange     AUS  OWF
7   Orange     AUS  PMR
8   Orange     AUS  KIQ
9      Red     AUS  DOG
10     Red     AUS  MOP
11     Red     AUS  LOF
12     Red     RUS  DOG
13     Red     RUS  MOP
14     Red     RUS  LOF
15     Red     NZL  DOG
16     Red     NZL  MOP
17     Red     NZL  LOF
0 голосов
/ 01 июля 2018

Вот одно решение, использующее itertools.product. Это не требует промежуточного фрейма данных, который вы создали.

from itertools import chain, product

df = pd.DataFrame({'org': ['Orange', 'Red'],
                   'country': ['USA, GBR, AUS', 'AUS, RUS, NZL'],
                   'type': ['OWF, PMR, KIQ', 'DOG, MOP, LOF']})

split1 = df['country'].str.split(', ')
split2 = df['type'].str.split(', ')

lens = split1.map(len) * split2.map(len)

c_list, t_list = zip(*chain.from_iterable(map(product, split1, split2)))

res = pd.DataFrame({'org': np.repeat(df['org'], lens),
                    'country': c_list,
                    'type': t_list})

Объяснение

Волшебство происходит с этой строкой:

c_list, t_list = zip(*chain.from_iterable(map(product, split1, split2)))

Работа изнутри:

  • Рассчитать декартово произведение для каждой пары предметов по split1 / split2.
  • Объедините их в цепочку с не вложенными итерациями результатов.
  • Распакуйте и застегните молнию на страны и типы.

Результат

print(res)

      org country type
0  Orange     USA  OWF
0  Orange     USA  PMR
0  Orange     USA  KIQ
0  Orange     GBR  OWF
0  Orange     GBR  PMR
0  Orange     GBR  KIQ
0  Orange     AUS  OWF
0  Orange     AUS  PMR
0  Orange     AUS  KIQ
1     Red     AUS  DOG
1     Red     AUS  MOP
1     Red     AUS  LOF
1     Red     RUS  DOG
1     Red     RUS  MOP
1     Red     RUS  LOF
1     Red     NZL  DOG
1     Red     NZL  MOP
1     Red     NZL  LOF
...