Python pandasの既存のDataFrameに新しい列を追加する


979

名前付きの列と行ではなく、連続した番号を持つ次のインデックス付きDataFrameがあります。

          a         b         c         d
2  0.671399  0.101208 -0.181532  0.241273
3  0.446172 -0.243316  0.051767  1.577318
5  0.614758  0.075793 -0.451460 -0.012493

'e'既存のデータフレームに新しい列を追加し、データフレーム内の何も変更したくない(つまり、新しい列は常にDataFrameと同じ長さです)。

0   -0.335485
1   -1.166658
2   -0.385571
dtype: float64

e上記の例に列を追加するにはどうすればよいですか?

回答:


1043

元のdf1インデックスを使用してシリーズを作成します。

df1['e'] = pd.Series(np.random.randn(sLength), index=df1.index)

Edit 2015
一部の人はSettingWithCopyWarning、このコードでを取得したと報告しています。
ただし、コードは現在のパンダバージョン0.16.1でも完全に実行されます。

>>> sLength = len(df1['a'])
>>> df1
          a         b         c         d
6 -0.269221 -0.026476  0.997517  1.294385
8  0.917438  0.847941  0.034235 -0.448948

>>> df1['e'] = pd.Series(np.random.randn(sLength), index=df1.index)
>>> df1
          a         b         c         d         e
6 -0.269221 -0.026476  0.997517  1.294385  1.757167
8  0.917438  0.847941  0.034235 -0.448948  2.228131

>>> p.version.short_version
'0.16.1'

SettingWithCopyWarning目的は、データフレームのコピーで、おそらく無効な割り当てを通知します。間違っているとは限りません(誤検知を引き起こす可能性があります)。ただし、0.13.0以降、同じ目的のためのより適切な方法があることを通知します。次に、警告が表示された場合は、そのアドバイスに従ってください。代わりに.loc [row_index、col_indexer] = valueを使用してみてください

>>> df1.loc[:,'f'] = pd.Series(np.random.randn(sLength), index=df1.index)
>>> df1
          a         b         c         d         e         f
6 -0.269221 -0.026476  0.997517  1.294385  1.757167 -0.050927
8  0.917438  0.847941  0.034235 -0.448948  2.228131  0.006109
>>> 

実際、パンダのドキュメントに記載されているように、これは現在より効率的な方法です


2017年を編集

コメントでかつ@Alexanderで示すように、現在の最善の方法は、使用する可能性がデータフレームの新しい列としてシリーズの値を追加するにはassign

df1 = df1.assign(e=pd.Series(np.random.randn(sLength)).values)

24
あなたがする必要がある場合は先頭に追加の列を使用DataFrame.insert:df1.insert(0、 'A'、シリーズ(np.random.randn(sLength)、インデックス= df1.index))
lowtech

29
以降のパンダバージョン0.12から、私はこの構文は最適ではなく、警告を与えると考えている:SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_index,col_indexer] = value instead
Zhubarb

6
SettingWithCopy警告として.locをたどると、何らかの警告が発生します。... self.obj [item_labels [indexer [info_axis]]] = value
seongjoo

12
あなたが解凍でき@toto_tico kwargs:そうのように、辞書をdf1 = df1.assign(**{'e': p.Series(np.random.randn(sLength)).values})
TCプロクター

23
「現在」と言ったり、年を参照したりするのではなく、パンダのバージョン番号を参照してください。たとえば、「0.14-0.16 do X、0.17 + do Y ...」
smci

229

これは、新しい列を追加する簡単な方法です。 df['e'] = e


154
投票数が多いにもかかわらず、この答えは間違っています。OPには非連続的なインデックスを持つデータフレームがあり、eSeries(np.random.randn(sLength)))はインデックスが付けられたシリーズ0-nを生成することに注意してください。これをdf1に割り当てると、いくつかのNaNセルが取得されます。
ホアキン2014

32
@joaquinの言うことは真実ですが、それを覚えている限り、これは非常に便利なショートカットです。
VedTopkar

2
@Eric Leschinski:どのように編集すればこの質問に役立つかわかりません。my_dataframe = pd.DataFrame(columns=('foo', 'bar'))。編集を
元に戻す

1
複数の行があり、割り当てを使用する場合、それは新しい列のすべての行にその値(あなたの場合はe)を割り当てます。これは通常望ましくないためです。
Paniz

156

新しい列「e」を既存のデータフレームに追加し、データフレーム内の何も変更しません。(シリーズは常にデータフレームと同じ長さになります。)

のインデックス値はのインデックス値とe一致すると仮定しますdf1

という新しい列を開始eし、シリーズの値を割り当てる最も簡単な方法はe次のとおりです。

df['e'] = e.values

割り当て(Pandas 0.16.0以降)

Pandas 0.16.0以降、を使用してassign、新しい列をDataFrameに割り当て、新しいオブジェクトに加えてすべての元の列を含む新しいオブジェクト(コピー)を返すこともできます。

df1 = df1.assign(e=e.values)

本実施例(これものソースコード含まれるassign機能)、また複数の列を含むことができます。

df = pd.DataFrame({'a': [1, 2], 'b': [3, 4]})
>>> df.assign(mean_a=df.a.mean(), mean_b=df.b.mean())
   a  b  mean_a  mean_b
0  1  3     1.5     3.5
1  2  4     1.5     3.5

あなたの例に関連して:

np.random.seed(0)
df1 = pd.DataFrame(np.random.randn(10, 4), columns=['a', 'b', 'c', 'd'])
mask = df1.applymap(lambda x: x <-0.7)
df1 = df1[-mask.any(axis=1)]
sLength = len(df1['a'])
e = pd.Series(np.random.randn(sLength))

>>> df1
          a         b         c         d
0  1.764052  0.400157  0.978738  2.240893
2 -0.103219  0.410599  0.144044  1.454274
3  0.761038  0.121675  0.443863  0.333674
7  1.532779  1.469359  0.154947  0.378163
9  1.230291  1.202380 -0.387327 -0.302303

>>> e
0   -1.048553
1   -1.420018
2   -1.706270
3    1.950775
4   -0.509652
dtype: float64

df1 = df1.assign(e=e.values)

>>> df1
          a         b         c         d         e
0  1.764052  0.400157  0.978738  2.240893 -1.048553
2 -0.103219  0.410599  0.144044  1.454274 -1.420018
3  0.761038  0.121675  0.443863  0.333674 -1.706270
7  1.532779  1.469359  0.154947  0.378163  1.950775
9  1.230291  1.202380 -0.387327 -0.302303 -0.509652

この新機能が最初に導入されたときの説明は、ここにあります


2
最初の方法(df['e'] = e.values)はデータフレームのコピーを作成しないが、2番目の方法(を使用df.assign)は作成することを考えると、2つの方法の相対的なパフォーマンスに関するコメントはありますか?多くの新しい列が順番に追加され、大きなデータフレームが存在する場合、最初の方法の方がはるかに優れたパフォーマンスを期待します。
jhin 2017年

2
@jhinはい、固定データフレームで作業している場合、直接割り当ては明らかに多くなります。を使用する利点はassign、操作をチェーン化する場合です。
Alexander

これは確かに、明示的と暗黙的の間の素晴らしいバランスのようです。+1:D
安倍ホフマン2017年

2
楽しみのためにdf.assign(**df.mean().add_prefix('mean_'))
piRSquared 2017年

1
@Owlright問題から、OPは単にデータフレームを連結し、インデックスを無視しているように見えます。この場合、上記の方法が機能します。インデックスを保持したい場合は、のようなものを使用してdf_new = pd.concat([df1, df2], axis=1)くださいignore_index=False
Alexander

51

最近のPandasバージョンではdf.assignを使用する方法になっているようです:

df1 = df1.assign(e=np.random.randn(sLength))

生産しませんSettingWithCopyWarning


1
@smciのコメントを上からコピーしています...「現在」と言ったり年を参照したりするのではなく、Pandasのバージョン番号を参照してください
Kyle C

50

これをNumPy経由で直接行うのが最も効率的です。

df1['e'] = np.random.randn(sLength)

私の元の(非常に古い)提案が使用することでしたmap(これははるかに遅いです):

df1['e'] = df1['a'].map(lambda x: np.random.random())

1
あなたの返信に感謝します。すでに述べたように、.map代わりに既存のシリーズを使用するようにコードを変更できますlambdaか?私がしようとしdf1['e'] = df1['a'].map(lambda x: e)たりdf1['e'] = df1['a'].map(e)、私が必要なものではないが、それ。(私はpyhonを初めて使用し、あなたの以前の回答はすでに私を助けてくれました)
tomasz74

@ tomasz74既にeシリーズとして持っている場合はmap、を使用する必要はありませんdf['e']=e(@joaquinsの回答)。
アンディヘイデン

49

超シンプルな列割り当て

パンダのデータフレームは、列の順序付けられた辞書として実装されます。

これは__getitem__ []、特定の列を取得するためだけでなく__setitem__ [] =、新しい列を割り当てるためにも使用できることを意味します。

たとえば、このデータフレームには、[]アクセサを使用するだけで列を追加できます

    size      name color
0    big      rose   red
1  small    violet  blue
2  small     tulip   red
3  small  harebell  blue

df['protected'] = ['no', 'no', 'no', 'yes']

    size      name color protected
0    big      rose   red        no
1  small    violet  blue        no
2  small     tulip   red        no
3  small  harebell  blue       yes

これは、データフレームのインデックスがオフの場合でも機能することに注意してください。

df.index = [3,2,1,0]
df['protected'] = ['no', 'no', 'no', 'yes']
    size      name color protected
3    big      rose   red        no
2  small    violet  blue        no
1  small     tulip   red        no
0  small  harebell  blue       yes

[] =は進むべき道ですが、気を付けてください!

ただし、があり、pd.Seriesそれがインデックスがオフになっているデータフレームに割り当てようとすると、問題が発生します。例を参照してください:

df['protected'] = pd.Series(['no', 'no', 'no', 'yes'])
    size      name color protected
3    big      rose   red       yes
2  small    violet  blue        no
1  small     tulip   red        no
0  small  harebell  blue        no

これはpd.Series、デフォルトでaに0からnまで列挙されたインデックスがあるためです。そしてパンダの[] =方法 「スマート」にしようとします

実際に起こっていること。

あなたは使用する場合[] =、メソッドパンダを静かに左側のデータフレームと右手シリーズのインデックスのインデックスを使用して外部結合または外側のマージを実行しています。df['column'] = series

サイドノート

この[]=メソッドは入力に応じてさまざまなことを実行しようとするため、これはすぐに認知的不協和を引き起こし、パンダのしくみを知らないと結果を予測できません。したがって[]=、コードベースについてはアドバイスしますが、ノートブックでデータを探索する場合は問題ありません。

問題を回避する

があり、pd.Seriesそれを上から下に割り当てたい場合、または生産的なコードをコーディングしていて、インデックスの順序がわからない場合は、この種の問題を保護する価値があります。

pd.Seriesをa np.ndarrayまたはにダウンキャストすることができlistます。これでうまくいきます。

df['protected'] = pd.Series(['no', 'no', 'no', 'yes']).values

または

df['protected'] = list(pd.Series(['no', 'no', 'no', 'yes']))

しかし、これはあまり明確ではありません。

一部のコーダーがやって来て、「ちょっと、これは冗長に見えます、私はこれを最適化します」と言うかもしれません。

明示的な方法

のインデックスをのインデックスに設定pd.Seriesすることdfは明示的です。

df['protected'] = pd.Series(['no', 'no', 'no', 'yes'], index=df.index)

または、より現実的には、おそらくpd.Seriesすでに利用可能です。

protected_series = pd.Series(['no', 'no', 'no', 'yes'])
protected_series.index = df.index

3     no
2     no
1     no
0    yes

割り当て可能になりました

df['protected'] = protected_series

    size      name color protected
3    big      rose   red        no
2  small    violet  blue        no
1  small     tulip   red        no
0  small  harebell  blue       yes

代替方法 df.reset_index()

インデックスの不協和が問題であるため、データフレームのインデックスが物事を指示するべきではないと感じた場合は、単にインデックスを削除できます。これは高速になるはずですが、関数はおそらく 2つのことを行うため、あまりクリーンではありません。

df.reset_index(drop=True)
protected_series.reset_index(drop=True)
df['protected'] = protected_series

    size      name color protected
0    big      rose   red        no
1  small    violet  blue        no
2  small     tulip   red        no
3  small  harebell  blue       yes

注意事項 df.assign

一方でdf.assignあなたは何をしているか、メイク、それはより明示的な、それは実際に上記のように、すべて同じ問題を抱えています[]=

df.assign(protected=pd.Series(['no', 'no', 'no', 'yes']))
    size      name color protected
3    big      rose   red       yes
2  small    violet  blue        no
1  small     tulip   red        no
0  small  harebell  blue        no

df.assign列が呼び出されないことに注意してくださいself。エラーが発生します。関数にこれらの種類のアーティファクトがあるため、これはdf.assign 臭いになります。

df.assign(self=pd.Series(['no', 'no', 'no', 'yes'])
TypeError: assign() got multiple values for keyword argument 'self'

あなたは「まあ、私はその時だけは使わないだろう」と言うかもしれませんself。しかし、新しい議論をサポートするために、この関数が将来どのように変化するかを知っている人はいます。多分あなたの列名はパンダの新しい更新の引数になり、アップグレードで問題を引き起こします。


6
メソッドpandas を使用すると、[] =静かに外部結合または外部マージが実行されます」。これは、トピック全体で最も重要な情報です。しかし、[]=オペレーターの動作に関する公式ドキュメントへのリンクを提供できますか?
ライトマン2017


23

新しい列全体を初期のベース値(などNone)に設定する場合は、次のようにできます。df1['e'] = None

これにより、実際には「オブジェクト」タイプがセルに割り当てられます。したがって、後で、リストなどの複雑なデータ型を個々のセルに自由に入力できます。


1
これにより、copywarning
00__00__00

1
df ['E'] = ''は、誰かが空の列を追加したい場合にも機能します
debaonline4u

21

私は恐ろしいを得ましたSettingWithCopyWarning、そしてそれはiloc構文を使用することによって修正されませんでした。私のDataFrameは、read_sqlによってODBCソースから作成されました。上記のlowtechの提案を使用すると、以下がうまくいきました:

df.insert(len(df.columns), 'e', pd.Series(np.random.randn(sLength),  index=df.index))

これは列を最後に挿入するためにうまくいきました。それが最も効率的かどうかはわかりませんが、警告メッセージは好きではありません。より良い解決策があると思いますが、それを見つけることができません。インデックスのいくつかの側面に依存すると思います。
。これは1回だけ機能し、既存の列を上書きしようとするとエラーメッセージが表示されます。
上記のように、0.16.0以降は、割り当てが最良のソリューションです。ドキュメントを参照してくださいhttp://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.assign.html#pandas.DataFrame.assign 中間値を上書きしないデータフロータイプに適しています。


これが2019年に私のために働く唯一の方法です!
ヒドラドン

14
  1. 最初に、list_of_e関連するデータを持つPythonを作成します。
  2. これを使って: df['e'] = list_of_e

1
私は本当に理解していません、なぜこれが好ましい答えではないのですか?pd.Seriesがある場合は、tolist()コマンドが役立つ場合があります。
したがって、S

11

追加しようとしている列が系列変数の場合は、次のようにします。

df["new_columns_name"]=series_variable_name #this will do it for you

これは、既存の列を置き換える場合でもうまく機能します。置き換える列と同じnew_columns_nameを入力するだけで、既存の列データが新しい系列データで上書きされます。


10

データフレームおよびSeriesオブジェクトが持っている場合は、同じインデックスをpandas.concatここにも動作します:

import pandas as pd
df
#          a            b           c           d
#0  0.671399     0.101208   -0.181532    0.241273
#1  0.446172    -0.243316    0.051767    1.577318
#2  0.614758     0.075793   -0.451460   -0.012493

e = pd.Series([-0.335485, -1.166658, -0.385571])    
e
#0   -0.335485
#1   -1.166658
#2   -0.385571
#dtype: float64

# here we need to give the series object a name which converts to the new  column name 
# in the result
df = pd.concat([df, e.rename("e")], axis=1)
df

#          a            b           c           d           e
#0  0.671399     0.101208   -0.181532    0.241273   -0.335485
#1  0.446172    -0.243316    0.051767    1.577318   -1.166658
#2  0.614758     0.075793   -0.451460   -0.012493   -0.385571

それらが同じインデックスを持っていない場合:

e.index = df.index
df = pd.concat([df, e.rename("e")], axis=1)

10

フールプルーフ:

df.loc[:, 'NewCol'] = 'New_Val'

例:

df = pd.DataFrame(data=np.random.randn(20, 4), columns=['A', 'B', 'C', 'D'])

df

           A         B         C         D
0  -0.761269  0.477348  1.170614  0.752714
1   1.217250 -0.930860 -0.769324 -0.408642
2  -0.619679 -1.227659 -0.259135  1.700294
3  -0.147354  0.778707  0.479145  2.284143
4  -0.529529  0.000571  0.913779  1.395894
5   2.592400  0.637253  1.441096 -0.631468
6   0.757178  0.240012 -0.553820  1.177202
7  -0.986128 -1.313843  0.788589 -0.707836
8   0.606985 -2.232903 -1.358107 -2.855494
9  -0.692013  0.671866  1.179466 -1.180351
10 -1.093707 -0.530600  0.182926 -1.296494
11 -0.143273 -0.503199 -1.328728  0.610552
12 -0.923110 -1.365890 -1.366202 -1.185999
13 -2.026832  0.273593 -0.440426 -0.627423
14 -0.054503 -0.788866 -0.228088 -0.404783
15  0.955298 -1.430019  1.434071 -0.088215
16 -0.227946  0.047462  0.373573 -0.111675
17  1.627912  0.043611  1.743403 -0.012714
18  0.693458  0.144327  0.329500 -0.655045
19  0.104425  0.037412  0.450598 -0.923387


df.drop([3, 5, 8, 10, 18], inplace=True)

df

           A         B         C         D
0  -0.761269  0.477348  1.170614  0.752714
1   1.217250 -0.930860 -0.769324 -0.408642
2  -0.619679 -1.227659 -0.259135  1.700294
4  -0.529529  0.000571  0.913779  1.395894
6   0.757178  0.240012 -0.553820  1.177202
7  -0.986128 -1.313843  0.788589 -0.707836
9  -0.692013  0.671866  1.179466 -1.180351
11 -0.143273 -0.503199 -1.328728  0.610552
12 -0.923110 -1.365890 -1.366202 -1.185999
13 -2.026832  0.273593 -0.440426 -0.627423
14 -0.054503 -0.788866 -0.228088 -0.404783
15  0.955298 -1.430019  1.434071 -0.088215
16 -0.227946  0.047462  0.373573 -0.111675
17  1.627912  0.043611  1.743403 -0.012714
19  0.104425  0.037412  0.450598 -0.923387

df.loc[:, 'NewCol'] = 0

df
           A         B         C         D  NewCol
0  -0.761269  0.477348  1.170614  0.752714       0
1   1.217250 -0.930860 -0.769324 -0.408642       0
2  -0.619679 -1.227659 -0.259135  1.700294       0
4  -0.529529  0.000571  0.913779  1.395894       0
6   0.757178  0.240012 -0.553820  1.177202       0
7  -0.986128 -1.313843  0.788589 -0.707836       0
9  -0.692013  0.671866  1.179466 -1.180351       0
11 -0.143273 -0.503199 -1.328728  0.610552       0
12 -0.923110 -1.365890 -1.366202 -1.185999       0
13 -2.026832  0.273593 -0.440426 -0.627423       0
14 -0.054503 -0.788866 -0.228088 -0.404783       0
15  0.955298 -1.430019  1.434071 -0.088215       0
16 -0.227946  0.047462  0.373573 -0.111675       0
17  1.627912  0.043611  1.743403 -0.012714       0
19  0.104425  0.037412  0.450598 -0.923387       0

2
絶対確実ではありません。これはOPの質問には対応していません。これは、既存のデータフレームと新しいシリーズのインデックスが揃っていない場合です。
アレクサンダー

7

私はちょうどちょうどのためのように、それを追加してみましょうhum3.loc解決していないSettingWithCopyWarningと私はに頼らなければなりませんでしたdf.insert()。私の場合、偽陽性は「偽の」チェーンインデックスによって生成されました。 dict['a']['e']ここ'e'で、は新しい列でありdict['a']、辞書からのDataFrameです。

また、実行していることがわかっている場合は、pd.options.mode.chained_assignment = None ここに記載されている他のソリューションのいずれかを使用して警告を切り替えることができ ます。


7

データフレームの特定の場所(0 <= loc <=列数)に新しい列を挿入するには、Dataframe.insertを使用します。

DataFrame.insert(loc, column, value)

したがって、dfというデータフレームの最後に列eを追加する場合は、次のように使用できます。

e = [-0.335485, -1.166658, -0.385571]    
DataFrame.insert(loc=len(df.columns), column='e', value=e)

は、シリーズ、整数(この場合、すべてのセルがこの1つの値で埋められる)、または配列のような構造になります。

https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.insert.html


6

新しい列を割り当てる前に、インデックス付きのデータがある場合は、インデックスをソートする必要があります。少なくとも私の場合、私はしなければなりませんでした:

data.set_index(['index_column'], inplace=True)
"if index is unsorted, assignment of a new column will fail"        
data.sort_index(inplace = True)
data.loc['index_value1', 'column_y'] = np.random.randn(data.loc['index_value1', 'column_x'].shape[0])

6

ただし、注意すべきことは、

df1['e'] = Series(np.random.randn(sLength), index=df1.index)

これは事実上、df1.indexの結合になります。したがって、外部結合効果が必要な場合、おそらく不完全な解決策は、データの全体をカバーするインデックス値を持つデータフレームを作成してから、上記のコードを使用することです。例えば、

data = pd.DataFrame(index=all_possible_values)
df1['e'] = Series(np.random.randn(sLength), index=df1.index)

5

numpy.nanダムを取得せずにsの列をデータフレームに追加する一般的な方法を探していましたSettingWithCopyWarning

以下から:

私はこれを思いつきました:

col = 'column_name'
df = df.assign(**{col:numpy.full(len(df), numpy.nan)})

4

新しい列「e」を既存のデータフレームに追加するには

 df1.loc[:,'e'] = Series(np.random.randn(sLength))

また、警告メッセージが表示されます
B Furtado 2017

df1.loc [::、 'e'] = Series(np.random.randn(sLength))を使用する必要があります
Hermes Morales

4

完全を期すために、DataFrame.eval()メソッドを使用したさらに別のソリューション:

データ:

In [44]: e
Out[44]:
0    1.225506
1   -1.033944
2   -0.498953
3   -0.373332
4    0.615030
5   -0.622436
dtype: float64

In [45]: df1
Out[45]:
          a         b         c         d
0 -0.634222 -0.103264  0.745069  0.801288
4  0.782387 -0.090279  0.757662 -0.602408
5 -0.117456  2.124496  1.057301  0.765466
7  0.767532  0.104304 -0.586850  1.051297
8 -0.103272  0.958334  1.163092  1.182315
9 -0.616254  0.296678 -0.112027  0.679112

解決:

In [46]: df1.eval("e = @e.values", inplace=True)

In [47]: df1
Out[47]:
          a         b         c         d         e
0 -0.634222 -0.103264  0.745069  0.801288  1.225506
4  0.782387 -0.090279  0.757662 -0.602408 -1.033944
5 -0.117456  2.124496  1.057301  0.765466 -0.498953
7  0.767532  0.104304 -0.586850  1.051297 -0.373332
8 -0.103272  0.958334  1.163092  1.182315  0.615030
9 -0.616254  0.296678 -0.112027  0.679112 -0.622436


3

以下は私がしたことです...しかし、私はパンダと一般的に本当にPythonにかなり新しいので、約束はありません。

df = pd.DataFrame([[1, 2], [3, 4], [5,6]], columns=list('AB'))

newCol = [3,5,7]
newName = 'C'

values = np.insert(df.values,df.shape[1],newCol,axis=1)
header = df.columns.values.tolist()
header.append(newName)

df = pd.DataFrame(values,columns=header)

3

を取得したSettingWithCopyWarning場合、簡単な修正は、列を追加しようとしているDataFrameをコピーすることです。

df = df.copy()
df['col_name'] = values

10
それは良い考えではありません。データフレームが十分に大きい場合、メモリを大量に消費することになります...さらに、時々列を追加し続けると、悪夢に変わるでしょう。
Kevad
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.