Превращение числового диапазона в последовательный диапазон

#python #pandas #numpy

#python #pandas #numpy

Вопрос:

У меня есть набор данных около 9000 с диапазонами территорий вида [1-5, 10-99,100-115] Я хочу расширить данные и строки, чтобы они имели форму в предоставленном наборе данных.

Моей первой мыслью было, возможно, перебирать серию ‘Terri’ и запускать ее через pd.series(range(i,100)), но это не привело бы к приведенному ниже результату.

Ценю помощь.

 import pandas as pd
d={'Peril':['Fire','Wind'],'Terri':[1-5,6-10],'Premium':[100,200]}
output={'Peril':['Fire','Fire','Fire','Fire','Fire','Wind','Wind','Wind','Wind','Wind'],'Terri':[1,2,3,4,5,6,7,8,9,10],'Premium':[100,100,100,100,100,200,200,200,200,200]}
df=pd.DataFrame(data=d)
expected_output=pd.DataFrame(data=output)
 

Комментарии:

1. [1-5,6-10] точно так же, как [-4,-4] . Это реальное представление ваших данных или вы имеете в виду ['1-5','6-10'] ?

2. Можете ли вы лучше уточнить ожидаемые входные и выходные данные

3. Извинения, [‘1-5′,’6-10’] это правильная интерпретация

Ответ №1:

Используйте пару вспомогательных списков, затем используйте, и pandas.index.repeat DataFrame.assign numpy.hstack :

 import numpy as np
import pandas as pd

ranges = [np.arange(s, e 1) for s, e in [list(map(int, x)) for x in df.Terri.str.split('-')]]
lens = [len(x) for x in ranges]

df_new = df.loc[df.index.repeat(lens)].assign(Terri=np.hstack(ranges))
 

[выход]

   Peril  Terri  Premium
0  Fire      1      100
0  Fire      2      100
0  Fire      3      100
0  Fire      4      100
0  Fire      5      100
1  Wind      6      200
1  Wind      7      200
1  Wind      8      200
1  Wind      9      200
1  Wind     10      200
 

Для справки, ranges выглядит так:

 [array([1, 2, 3, 4, 5]), array([ 6,  7,  8,  9, 10])]
 

lens выглядит так:

 [5, 5]
 

Ответ №2:

Предполагая, что Terri он должен содержать диапазоны строк, вместо операций вычитания вы можете создать фрейм данных из ваших диапазонов, а затем stack воспользоваться join общим индексом для расширения исходного фрейма.


 u = df['Terri'].str.split('-', expand=True).astype(int).values

j = pd.DataFrame(
    [np.arange(start, stop 1) for start, stop in u]
)

j.stack().reset_index(1, drop=True).to_frame('Terri')

df.drop('Terri', 1).join(f)
 
   Peril  Premium  Terri
0  Fire      100      1
0  Fire      100      2
0  Fire      100      3
0  Fire      100      4
0  Fire      100      5
1  Wind      200      6
1  Wind      200      7
1  Wind      200      8
1  Wind      200      9
1  Wind      200     10
 

Поскольку поведение stack будет отбрасывать нулевые значения, ваши диапазоны не обязательно должны быть одинаковой длины.

Ответ №3:

Единственный способ, которым это работает, — это если вы запускаете Terri столбец DataFrame в виде строки:

 d={'Peril':['Fire','Wind'],'Terri':['1-5','6-10'],'Premium':[100,200]}
df = pd.DataFrame(d)
print(df)
#  Peril Terri  Premium
#0  Fire   1-5      100
#1  Wind  6-10      200
 

Если вы разделите строку в Terri столбце на - , вы можете использовать ее в качестве входных данных range , за исключением того, что вам нужно будет добавить единицу к значению остановки, чтобы включить конечную точку. Чтобы упростить это, вы можете определить свою собственную функцию диапазона:

 def myRange(a, b):
    return range(a, b 1)
 

Теперь вы можете разделить столбец, применить myRange функцию и сложить результат:

 temp = pd.DataFrame(
    df['Terri'].str.split("-")
        .apply(lambda x: pd.Series(myRange(*map(int, x))))
        .stack()
        .reset_index(level=1, drop=True),
    columns=["Terri"]
)
print(temp)
#   Terri
#0      1
#0      2
#0      3
#0      4
#0      5
#1      6
#1      7
#1      8
#1      9
#1     10
 

Наконец, соедините этот результат с вашим исходным фреймом данных:

 print(df.drop(["Terri"], axis=1).join(temp))
#  Peril  Premium  Terri
#0  Fire      100      1
#0  Fire      100      2
#0  Fire      100      3
#0  Fire      100      4
#0  Fire      100      5
#1  Wind      200      6
#1  Wind      200      7
#1  Wind      200      8
#1  Wind      200      9
#1  Wind      200     10
 

То же самое, сжатый:

 df.drop(["Terri"], axis=1).join(
    pd.DataFrame(
        df['Terri'].str.split("-")
            .apply(lambda x: pd.Series(myRange(*map(int, x))))
            .stack()
            .reset_index(level=1, drop=True),
        columns=["Terri"]
    )
)
 

Ответ №4:

Если [1-5,6-10] это действительно так ['1-5','6-10'] , то приведенный ниже код может работать:

 new_df = []
for row in df.iterrows():
    rng = row[1]['Terri']
    rng = rng.split('-')
    start, end = int(rng[0]), int(rng[1])
    for n in range(start, end 1):
        new_row = {
            'Peril': row[1]['Peril'],
            'Terri': n,
            'Premium': row[1]['Premium'],
        }
        new_df.append(new_row)
output = pd.DataFrame(new_df)