Расширение встроенного списка в широкий формат данных в python [duplicate]

У вас есть правильная общая стратегия для этого, используя базовую графику, но, как было указано, вы, по сути, говорите R, чтобы выбрать случайный цвет из набора по 10 для каждой строки. Учитывая это, неудивительно, что вы будете иногда получать две строки одного цвета. Вот пример использования базовой графики:

plot(0,0,xlim = c(-10,10),ylim = c(-10,10),type = "n")

cl <- rainbow(5)

for (i in 1:5){
    lines(-10:10,runif(21,-10,10),col = cl[i],type = 'b')
}

enter image description here [/g0]

Обратите внимание на использование функции type = "n" для подавления всех графиков в исходном вызове для настройки окно и индексирование cl внутри цикла for.

48
задан maxymoo 9 September 2015 в 00:22
поделиться

8 ответов

В приведенном ниже коде я сначала сбросил индекс, чтобы упростить итерацию строк.

Создаю список списков, в которых каждый элемент внешнего списка представляет собой строку целевого DataFrame, и каждый элемент внутреннего списка является одним из столбцов. Этот вложенный список в конечном итоге будет объединен для создания желаемого DataFrame.

Я использую функцию lambda вместе со итерацией списка, чтобы создать строку для каждого элемента nearest_neighbors в паре с соответствующим name и opponent.

Наконец, я создаю новый DataFrame из этого списка (используя исходные имена столбцов и вернув индекс в name и opponent).

df = (pd.DataFrame({'name': ['A.J. Price'] * 3, 
                    'opponent': ['76ers', 'blazers', 'bobcats'], 
                    'nearest_neighbors': [['Zach LaVine', 'Jeremy Lin', 'Nate Robinson', 'Isaia']] * 3})
      .set_index(['name', 'opponent']))

>>> df
                                                    nearest_neighbors
name       opponent                                                  
A.J. Price 76ers     [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia]
           blazers   [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia]
           bobcats   [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia]

df.reset_index(inplace=True)
rows = []
_ = df.apply(lambda row: [rows.append([row['name'], row['opponent'], nn]) 
                         for nn in row.nearest_neighbors], axis=1)
df_new = pd.DataFrame(rows, columns=df.columns).set_index(['name', 'opponent'])

>>> df_new
                    nearest_neighbors
name       opponent                  
A.J. Price 76ers          Zach LaVine
           76ers           Jeremy Lin
           76ers        Nate Robinson
           76ers                Isaia
           blazers        Zach LaVine
           blazers         Jeremy Lin
           blazers      Nate Robinson
           blazers              Isaia
           bobcats        Zach LaVine
           bobcats         Jeremy Lin
           bobcats      Nate Robinson
           bobcats              Isaia

EDIT JUNE 2017

Альтернативный метод выглядит следующим образом:

>>> (pd.melt(df.nearest_neighbors.apply(pd.Series).reset_index(), 
             id_vars=['name', 'opponent'],
             value_name='nearest_neighbors')
     .set_index(['name', 'opponent'])
     .drop('variable', axis=1)
     .dropna()
     .sort_index()
     )
37
ответ дан Alexander 20 August 2018 в 15:46
поделиться

Расширение ответа Олега .iloc на автоматическое выравнивание всех столбцов-столбцов:

def extend_iloc(df):
    cols_to_flatten = [colname for colname in df.columns if 
    isinstance(df.iloc[0][colname], list)]
    # Row numbers to repeat 
    lens = df[cols_to_flatten[0]].apply(len)
    vals = range(df.shape[0])
    ilocations = np.repeat(vals, lens)
    # Replicate rows and add flattened column of lists
    with_idxs = [(i, c) for (i, c) in enumerate(df.columns) if c not in cols_to_flatten]
    col_idxs = list(zip(*with_idxs)[0])
    new_df = df.iloc[ilocations, col_idxs].copy()

    # Flatten columns of lists
    for col_target in cols_to_flatten:
        col_flat = [item for sublist in df[col_target] for item in sublist]
        new_df[col_target] = col_flat

    return new_df

Предполагается, что каждый столбец списка имеет равную длину списка.

0
ответ дан Brian Atwood 20 August 2018 в 15:46
поделиться

Я думаю, что это действительно хороший вопрос, в Hive вы бы использовали EXPLODE, я думаю, что есть случай, чтобы Pandas включил эту функциональность по умолчанию. Вы можете взорвать столбец списка следующим образом:

import numpy as np

df = pd.DataFrame({'listcol':[[1,2,3],[4,5,6]]})
X = pd.concat([pd.DataFrame(v, index=np.repeat(k,len(v))) 
            for k,v in df.listcol.to_dict().items()])    

Затем вы можете использовать pd.merge, чтобы присоединиться к этому исходному файловому кадру, как @helpanderr, предложенный в комментарии к исходному вопросу.

9
ответ дан maxymoo 20 August 2018 в 15:46
поделиться

Самый быстрый метод, который я нашел до сих пор, расширяет DataFrame с помощью .iloc и назначает обратно сглаженный целевой столбец.

Учитывая обычный вход (реплицированный бит):

df = (pd.DataFrame({'name': ['A.J. Price'] * 3, 
                    'opponent': ['76ers', 'blazers', 'bobcats'], 
                    'nearest_neighbors': [['Zach LaVine', 'Jeremy Lin', 'Nate Robinson', 'Isaia']] * 3})
      .set_index(['name', 'opponent']))
df = pd.concat([df]*10)

df
Out[3]: 
                                                   nearest_neighbors
name       opponent                                                 
A.J. Price 76ers     [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia]
           blazers   [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia]
           bobcats   [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia]
           76ers     [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia]
           blazers   [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia]
...

Учитывая следующие предложенные альтернативы:

col_target = 'nearest_neighbors'

def extend_iloc():
    # Flatten columns of lists
    col_flat = [item for sublist in df[col_target] for item in sublist] 
    # Row numbers to repeat 
    lens = df[col_target].apply(len)
    vals = range(df.shape[0])
    ilocations = np.repeat(vals, lens)
    # Replicate rows and add flattened column of lists
    cols = [c for c in df.columns if c != col_target]
    new_df = df.iloc[ilocations, cols].copy()
    new_df[col_target] = col_flat
    return new_df

def melt():
    return (pd.melt(df[col_target].apply(pd.Series).reset_index(), 
             id_vars=['name', 'opponent'],
             value_name=col_target)
            .set_index(['name', 'opponent'])
            .drop('variable', axis=1)
            .dropna()
            .sort_index())

def stack_unstack():
    return (df[col_target].apply(pd.Series)
            .stack()
            .reset_index(level=2, drop=True)
            .to_frame(col_target))

Я обнаружил, что extend_iloc() является самым быстрым:

%timeit extend_iloc()
3.11 ms ± 544 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit melt()
22.5 ms ± 1.25 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit stack_unstack()
11.5 ms ± 410 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
5
ответ дан Oleg 20 August 2018 в 15:46
поделиться

Более альтернативное решение с применением (pd.Series):

df = pd.DataFrame({'listcol':[[1,2,3],[4,5,6]]})

# expand df.listcol into its own dataframe
tags = df['listcol'].apply(pd.Series)

# rename each variable is listcol
tags = tags.rename(columns = lambda x : 'listcol_' + str(x))

# join the tags dataframe back to the original dataframe
df = pd.concat([df[:], tags[:]], axis=1)
7
ответ дан Philipp Schwarz 20 August 2018 в 15:46
поделиться

Вот потенциальная оптимизация для больших кадров данных. Это выполняется быстрее, когда в поле «взрыва» есть несколько равных значений. (Чем больше размер кадра данных сравнивается с уникальным значением в поле, тем лучше будет работать этот код.)

def lateral_explode(dataframe, fieldname): 
    temp_fieldname = fieldname + '_made_tuple_' 
    dataframe[temp_fieldname] = dataframe[fieldname].apply(tuple)       
    list_of_dataframes = []
    for values in dataframe[temp_fieldname].unique().tolist(): 
        list_of_dataframes.append(pd.DataFrame({
            temp_fieldname: [values] * len(values), 
            fieldname: list(values), 
        }))
    dataframe = dataframe[list(set(dataframe.columns) - set([fieldname]))]\ 
        .merge(pd.concat(list_of_dataframes), how='left', on=temp_fieldname) 
    del dataframe[temp_fieldname]

    return dataframe
0
ответ дан Sinan Ozel 20 August 2018 в 15:46
поделиться

Подобно функциональности EXPLODE от Hive:

import copy

def pandas_explode(df, column_to_explode):
    """
    Similar to Hive's EXPLODE function, take a column with iterable elements, and flatten the iterable to one element 
    per observation in the output table

    :param df: A dataframe to explod
    :type df: pandas.DataFrame
    :param column_to_explode: 
    :type column_to_explode: str
    :return: An exploded data frame
    :rtype: pandas.DataFrame
    """

    # Create a list of new observations
    new_observations = list()

    # Iterate through existing observations
    for row in df.to_dict(orient='records'):

        # Take out the exploding iterable
        explode_values = row[column_to_explode]
        del row[column_to_explode]

        # Create a new observation for every entry in the exploding iterable & add all of the other columns
        for explode_value in explode_values:

            # Deep copy existing observation
            new_observation = copy.deepcopy(row)

            # Add one (newly flattened) value from exploding iterable
            new_observation[column_to_explode] = explode_value

            # Add to the list of new observations
            new_observations.append(new_observation)

    # Create a DataFrame
    return_df = pandas.DataFrame(new_observations)

    # Return
    return return_df
3
ответ дан Xenolion 20 August 2018 в 15:46
поделиться
  • 1
    Когда я запускаю это, я получаю следующую ошибку: NameError: global name 'copy' is not defined – frmsaul 8 August 2017 в 17:53

Используйте apply(pd.Series) и stack, затем reset_index и to_frame

In [1803]: (df.nearest_neighbors.apply(pd.Series)
              .stack()
              .reset_index(level=2, drop=True)
              .to_frame('nearest_neighbors'))
Out[1803]:
                    nearest_neighbors
name       opponent
A.J. Price 76ers          Zach LaVine
           76ers           Jeremy Lin
           76ers        Nate Robinson
           76ers                Isaia
           blazers        Zach LaVine
           blazers         Jeremy Lin
           blazers      Nate Robinson
           blazers              Isaia
           bobcats        Zach LaVine
           bobcats         Jeremy Lin
           bobcats      Nate Robinson
           bobcats              Isaia

Подробности

In [1804]: df
Out[1804]:
                                                   nearest_neighbors
name       opponent
A.J. Price 76ers     [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia]
           blazers   [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia]
           bobcats   [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia]
14
ответ дан Zero 20 August 2018 в 15:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: