কীভাবে একটি ডেটাফ্রেম সেলের ভিতরে একটি তালিকা পৃথক সারিতে বিস্ফোরিত করা যায়


95

আমি সেই মানগুলির প্রত্যেকটির জন্য সারণীতে একটি তালিকাযুক্ত একটি পান্ডাস সেলটি সন্ধান করছি।

সুতরাং, এটি নিন:

এখানে চিত্র বর্ণনা লিখুন

আমি যদি nearest_neighborsকলামে মানগুলি আনপ্যাক এবং স্ট্যাক করতে চাই যাতে প্রতিটি মান প্রতিটি opponentসূচকের মধ্যে সারি হয় , তবে আমি কীভাবে সর্বোত্তম এটি করব? এই জাতীয় ক্রিয়াকলাপের জন্য বোঝানো কি পান্ডাস পদ্ধতি রয়েছে?


আপনি কি আপনার পছন্দসই আউটপুটটির উদাহরণ দিতে পারেন এবং আপনি এ পর্যন্ত কী চেষ্টা করেছেন? আপনি যদি কিছু নমুনা ডেটা সরবরাহ করেন যা কেটে ও আটকানো যেতে পারে তবে অন্যদের পক্ষে আপনাকে সহায়তা করা সবচেয়ে সহজ।
ডাগ্রা

আপনি pd.DataFrame(df.nearest_neighbors.values.tolist())এই কলামটি আনপ্যাক করতে এবং তারপরে pd.mergeএটি অন্যদের সাথে আঠালো করতে ব্যবহার করতে পারেন ।
Hellpenderr

@ হেল্প্যান্ডার আমি values.tolist()এখানে কিছু করি না বলে মনে হয়; কলামটি ইতিমধ্যে একটি তালিকা
ম্যাক্সিমো

4
@ ম্যাক্সিমু i.imgur.com/YGQAYOY.png
নরকপান্ডার

4
সম্পর্কিত কিন্তু আরো বিস্তারিত ধারণ stackoverflow.com/questions/53218931/...
BEN_YO

উত্তর:


56

নীচের কোডে, আমি প্রথম সারিতে পুনরাবৃত্তি আরও সহজ করতে সূচি পুনরায় সেট করি।

আমি তালিকার একটি তালিকা তৈরি করি যেখানে বাইরের তালিকার প্রতিটি উপাদান লক্ষ্য ডাটাফ্রেমের সারি এবং অভ্যন্তরীণ তালিকার প্রতিটি উপাদান কলামগুলির মধ্যে একটি। এই নেস্টেড তালিকাটি শেষ পর্যন্ত পছন্দসই ডেটাফ্রেম তৈরি করতে সম্মত হবে।

প্রাসঙ্গিক এবং এর সাথে যুক্ত জোড় lambdaপ্রতিটি উপাদান জন্য একটি সারি তৈরি করতে আমি তালিকা পুনরাবৃত্তির সাথে একসাথে একটি ফাংশন ব্যবহার করি । nearest_neighborsnameopponent

অবশেষে, আমি এই তালিকা থেকে একটি নতুন 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

জুন জুন 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()
     )

apply(pd.Series)ক্ষুদ্রতম ফ্রেমের ক্ষেত্রে দুর্দান্ত, তবে কোনও যুক্তিসঙ্গত আকারের ফ্রেমের জন্য আপনার আরও পারফরম্যান্স সমাধানটি পুনর্বিবেচনা করা উচিত। দেখুন কখন আমার কোডে আমি পান্ডা প্রয়োগ করব ()? (প্রথমে কলামটি অনুকূলকরণের একটি আরও ভাল সমাধান।)
সিএস 95

4
তালিকার মতো কলামটি বিস্ফোরণটি পদ্ধতিটি যোগ করে প্যান্ডাসে 0.25-তে উল্লেখযোগ্যভাবে সরল করা হয়েছে explode()। আমি এখানে একই ডিএফ সেটআপ ব্যবহার করে একটি উদাহরণ সহ একটি উত্তর যুক্ত করেছি ।
joelostblom

শুনতে ভালো লাগল বর্তমান ব্যবহারের সাথে উদাহরণ যোগ করার জন্য ধন্যবাদ।
আলেকজান্ডার

37
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.explode('nearest_neighbors')

আউট:

                    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

4
মনে রাখবেন এটি কেবলমাত্র একটি একক কলামের জন্য কাজ করে (0.25 হিসাবে)। আরও সাধারণ সমাধানের জন্য এখানে এবং এখানে দেখুন ।
cs95

এটিই সবচেয়ে দ্রুততম সমাধান (সত্যিই যদি আপনার বিস্ফোরণে তালিকার একটি মাত্র কলাম থাকে বা এটি "মোড়কদ্বীপে যেমন খুলে ফেলা যায়")
এনেকুচেনিয়াস

পান্ডাস ডকুমেন্ট দ্বারা দ্রুততম সমাধান। তবে সাবধান: এক্সপ্লোডটি অন্তর্ভুক্ত নয়! বরং করুনdf = df.explode(...)
harmonica141

34

ব্যবহারের 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]

4
আপনার সমাধান কমনীয়তা পছন্দ! আপনি কি কোনও সুযোগের দ্বারা অন্যান্য পদ্ধতির বিরুদ্ধে এটি বেঞ্চমার্ক করেছেন?
আরপিআইঝি

4
ফলাফলটি df.nearest_neighbors.apply(pd.Series)আমার কাছে খুব অবাক করে;
কলম ইউ

4
@ আরপিজহ হ্যাঁ, এটি বেশ মার্জিত, তবে করুণভাবে ধীর।
cs95

16

আমি মনে করি এটি সত্যিই একটি ভাল প্রশ্ন, আপনি হাইভের মধ্যে ব্যবহার করবেন EXPLODE, আমি মনে করি একটি মামলা করার দরকার আছে যে পান্ডাদের ডিফল্টরূপে এই কার্যকারিতাটি অন্তর্ভুক্ত করা উচিত। আমি সম্ভবত নেস্টেড জেনারেটর বোঝার সাথে তালিকার কলামটি বিস্ফোরিত করব:

pd.DataFrame({
    "name": i[0],
    "opponent": i[1],
    "nearest_neighbor": neighbour
    }
    for i, row in df.iterrows() for neighbour in row.nearest_neighbors
    ).set_index(["name", "opponent"])

আমি পছন্দ করি যে কীভাবে এই সমাধানটি প্রতিটি সারির জন্য তালিকা আইটেমের সংখ্যা পৃথক হতে দেয়।
ব্যবহারকারী 1718097

এই পদ্ধতিটি দিয়ে মূল সূচক রাখার কোনও উপায় আছে কি?
গ্রীষ্মকালীন

4
@ সুমেরেলা এই সত্যিই পুরানো উত্তর ছিল, আমি এখন এটি কীভাবে করব তা দেখানোর জন্য আপডেট করেছি
maxymoo

4
@ ম্যাক্সিমো এটি এখনও একটি দুর্দান্ত প্রশ্ন, যদিও। আপডেট করার জন্য ধন্যবাদ!
গ্রীষ্মকালীন

আমি এটি দরকারী খুঁজে পেয়েছি এবং এটি একটি প্যাকেজে
ওরে

11

দ্রুততম পদ্ধতি আমি দেখেছি এতদূর সঙ্গে 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 = [i for i,c in enumerate(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)

চমৎকার মূল্যায়ন
স্টিফেনবশ

4
এর জন্য ধন্যবাদ, এটি সত্যই আমাকে সাহায্য করেছিল। আমি extend_iloc সমাধান ব্যবহার করা হয় এবং দেখা গেছে যে cols = [c for c in df.columns if c != col_target] হওয়া উচিত: ত্রুটি যদি কলাম সূচক সঙ্গে উপস্থাপন করা হয় না। cols = [i for i,c in enumerate(df.columns) if c != col_target]df.iloc[ilocations, cols].copy()
jdungan

আইলোক পরামর্শের জন্য আবার ধন্যবাদ। আমি এখানে এটি কীভাবে কাজ করে তার একটি বিশদ ব্যাখ্যা লিখেছি: माध्यम . com / @jhnadungan/… । আশা করি এটি একইরকম চ্যালেঞ্জ সহ যে কাউকে সহায়তা করবে।
jdungan

7

প্রয়োগ (পিডি.সিরিজ) সহ আরও ভাল বিকল্প সমাধান:

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)

এটি সারি নয় কলামগুলি প্রসারিত করে।
ওলেগ

@ পুরোপুরি ঠিক আছে, তবে আপনি সর্বদা ডেটাফ্রেম স্থানান্তর করতে পারেন এবং তারপরে পিডি প্রয়োগ করতে পারেন eriesসুররিস-অন্যান্য বেশিরভাগ পরামর্শের চেয়ে সহজ
ফিলিপ শোয়ার্জ

7

হাইভের এক্সপ্লোর কার্যকারিতাটির মতো:

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

4
আমি যখন এটি চালাই, আমি নিম্নলিখিত ত্রুটিটি NameError: global name 'copy' is not defined
পেয়েছি

4

সুতরাং এই সমস্ত উত্তর ভাল তবে আমি কিছু চাইছিলাম ^ সত্যিই সহজ simple তাই এখানে আমার অবদান:

def explode(series):
    return pd.Series([x for _list in series for x in _list])                               

এটাই .. আপনি যখন নতুন সিরিজ চান যেখানে তালিকাগুলি 'বিস্ফোরিত' হয়েছে কেবল তখনই এটি ব্যবহার করুন। এখানে একটি উদাহরণ যেখানে আমরা টাকো পছন্দগুলিতে value_counts () করি না :)

In [1]: my_df = pd.DataFrame(pd.Series([['a','b','c'],['b','c'],['c']]), columns=['tacos'])      
In [2]: my_df.head()                                                                               
Out[2]: 
   tacos
0  [a, b, c]
1     [b, c]
2        [c]

In [3]: explode(my_df['tacos']).value_counts()                                                     
Out[3]: 
c    3
b    2
a    1

2

বৃহত্তর ডেটাফ্রেমগুলির জন্য এখানে একটি সম্ভাব্য অপটিমাইজেশন। "বিস্ফোরক" ক্ষেত্রের বেশ কয়েকটি সমান মান থাকলে এটি দ্রুত চলে। (ক্ষেত্রের অনন্য মান গণনার সাথে ডেটাফ্রেমকে বৃহত্তর সাথে তুলনা করা হয়, এই কোডটি তত ভাল সম্পাদন করবে))

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

1

.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

এটি ধরে নেওয়া হয় যে প্রতিটি তালিকা-কলামের সমান তালিকার দৈর্ঘ্য রয়েছে।


1

প্রয়োগ (পিডি। সিরিজ) ব্যবহার না করে আপনি কলামটি সমতল করতে পারবেন। এটি কর্মক্ষমতা উন্নত করে।

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']))



%timeit (pd.DataFrame(df['nearest_neighbors'].values.tolist(), index = df.index)
           .stack()
           .reset_index(level = 2, drop=True).to_frame('nearest_neighbors'))

1.87 ms ± 9.74 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)


%timeit (df.nearest_neighbors.apply(pd.Series)
          .stack()
          .reset_index(level=2, drop=True)
          .to_frame('nearest_neighbors'))

2.73 ms ± 16.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

IndexError: অতিরিক্ত মাত্রা: ইনডেক্স, শুধুমাত্র 2 মাত্রা, না 3 আছে যখন আমি আমার উদাহরণ চেষ্টা
vinsent paramanantham

4
আপনার উদাহরণ অনুসারে আপনাকে রিসেট_ইন্ডেক্সে "স্তর" পরিবর্তন করতে হবে
ঘুমন্ত কুমার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.