标签归档:numpy

Python OpenCV2(cv2)包装器获取图像大小?

问题:Python OpenCV2(cv2)包装器获取图像大小?

如何cv2在Python OpenCV(numpy)的包装器中获取图像的大小。除了之外还有其他正确的方法吗numpy.shape()?如何获得以下格式的尺寸:(宽度,高度)列表?

How to get the size of an image in cv2 wrapper in Python OpenCV (numpy). Is there a correct way to do that other than numpy.shape(). How can I get it in these format dimensions: (width, height) list?


回答 0

cv2numpy用于处理图像,因此使用来获取图像大小的正确和最佳方法是numpy.shape。假设您正在使用BGR图像,下面是一个示例:

>>> import numpy as np
>>> import cv2
>>> img = cv2.imread('foo.jpg')
>>> height, width, channels = img.shape
>>> print height, width, channels
  600 800 3

如果您正在使用二进制图像,img它将具有两个尺寸,因此必须将代码更改为:height, width = img.shape

cv2 uses numpy for manipulating images, so the proper and best way to get the size of an image is using numpy.shape. Assuming you are working with BGR images, here is an example:

>>> import numpy as np
>>> import cv2
>>> img = cv2.imread('foo.jpg')
>>> height, width, channels = img.shape
>>> print height, width, channels
  600 800 3

In case you were working with binary images, img will have two dimensions, and therefore you must change the code to: height, width = img.shape


回答 1

恐怕没有“更好”的方法来获得这种大小,但是没有那么多痛苦。

当然,您的代码对于二进制/单图像以及多通道图像都应该是安全的,但是图像的主要尺寸始终以numpy数组的形状排在首位。如果您选择可读性,或者不想打扰它,可以将其包装在一个函数中,并为其命名,例如cv_size

import numpy as np
import cv2

# ...

def cv_size(img):
    return tuple(img.shape[1::-1])

如果您在终端机/ ipython上,还可以使用lambda表示它:

>>> cv_size = lambda img: tuple(img.shape[1::-1])
>>> cv_size(img)
(640, 480)

def交互工作时,用编写函数并不有趣。

编辑

本来我以为可以使用[:2],但是numpy的形状是(height, width[, depth]),并且我们需要(width, height)cv2.resize预期的那样-因此我们必须使用[1::-1]。难忘的是[:2]。还有谁记得反向切片?

I’m afraid there is no “better” way to get this size, however it’s not that much pain.

Of course your code should be safe for both binary/mono images as well as multi-channel ones, but the principal dimensions of the image always come first in the numpy array’s shape. If you opt for readability, or don’t want to bother typing this, you can wrap it up in a function, and give it a name you like, e.g. cv_size:

import numpy as np
import cv2

# ...

def cv_size(img):
    return tuple(img.shape[1::-1])

If you’re on a terminal / ipython, you can also express it with a lambda:

>>> cv_size = lambda img: tuple(img.shape[1::-1])
>>> cv_size(img)
(640, 480)

Writing functions with def is not fun while working interactively.

Edit

Originally I thought that using [:2] was OK, but the numpy shape is (height, width[, depth]), and we need (width, height), as e.g. cv2.resize expects, so – we must use [1::-1]. Even less memorable than [:2]. And who remembers reverse slicing anyway?


脾气暴躁的索引切片而不会丢失尺寸信息

问题:脾气暴躁的索引切片而不会丢失尺寸信息

我正在使用numpy,并希望在不丢失维度信息的情况下对行进行索引。

import numpy as np
X = np.zeros((100,10))
X.shape        # >> (100, 10)
xslice = X[10,:]
xslice.shape   # >> (10,)  

在此示例中,xslice现在为1维,但我希望它为(1,10)。在R中,我将使用X [10,:,drop = F]。numpy中是否有类似的东西。我在文档中找不到它,也没有看到类似的问题。

谢谢!

I’m using numpy and want to index a row without losing the dimension information.

import numpy as np
X = np.zeros((100,10))
X.shape        # >> (100, 10)
xslice = X[10,:]
xslice.shape   # >> (10,)  

In this example xslice is now 1 dimension, but I want it to be (1,10). In R, I would use X[10,:,drop=F]. Is there something similar in numpy. I couldn’t find it in the documentation and didn’t see a similar question asked.

Thanks!


回答 0

这可能最容易做到x[None, 10, :]或等效(但更具可读性)x[np.newaxis, 10, :]

就为什么不是默认值而言,我个人发现,不断拥有单例维度的数组会非常烦人。我猜想那些麻木的开发者也有同样的感觉。

另外,numpy可以很好地处理广播数组,因此通常没有理由保留切片所来自的数组的尺寸。如果您这样做了,那么类似:

a = np.zeros((100,100,10))
b = np.zeros(100,10)
a[0,:,:] = b

要么行不通,要么实施起来更加困难。

(或者至少这是我对切片时删除维度信息背后的numpy开发人员的猜测)

It’s probably easiest to do x[None, 10, :] or equivalently (but more readable) x[np.newaxis, 10, :].

As far as why it’s not the default, personally, I find that constantly having arrays with singleton dimensions gets annoying very quickly. I’d guess the numpy devs felt the same way.

Also, numpy handle broadcasting arrays very well, so there’s usually little reason to retain the dimension of the array the slice came from. If you did, then things like:

a = np.zeros((100,100,10))
b = np.zeros(100,10)
a[0,:,:] = b

either wouldn’t work or would be much more difficult to implement.

(Or at least that’s my guess at the numpy dev’s reasoning behind dropping dimension info when slicing)


回答 1

另一个解决方案是

X[[10],:]

要么

I = array([10])
X[I,:]

当通过索引列表(或数组)执行索引时,将保留数组的维数。这很好,因为它使您可以选择保持尺寸和压缩尺寸。

Another solution is to do

X[[10],:]

or

I = array([10])
X[I,:]

The dimensionality of an array is preserved when indexing is performed by a list (or an array) of indexes. This is nice because it leaves you with the choice between keeping the dimension and squeezing.


回答 2

我找到了一些合理的解决方案。

1)使用 numpy.take(X,[10],0)

2)使用这个奇怪的索引 X[10:11:, :]

理想情况下,这应该是默认设置。我从未理解过为什么尺寸会下降。但这是关于numpy的讨论…

I found a few reasonable solutions.

1) use numpy.take(X,[10],0)

2) use this strange indexing X[10:11:, :]

Ideally, this should be the default. I never understood why dimensions are ever dropped. But that’s a discussion for numpy…


回答 3

这是我更喜欢的替代方法。而不是使用单个数字编制索引,而是使用范围进行索引。即使用X[10:11,:]。(请注意,其中10:11不包括11)。

import numpy as np
X = np.zeros((100,10))
X.shape        # >> (100, 10)
xslice = X[10:11,:]
xslice.shape   # >> (1,10)

这也使得使用更多尺寸也很容易理解,而无需None费力地弄清楚要使用哪个索引的轴。同样,无需为数组大小做额外的记账工作,只需i:i+1i您将在常规索引中使用的任何记账工作做好。

b = np.ones((2, 3, 4))
b.shape # >> (2, 3, 4)
b[1:2,:,:].shape  # >> (1, 3, 4)
b[:, 2:3, :].shape .  # >> (2, 1, 4)

Here’s an alternative I like better. Instead of indexing with a single number, index with a range. That is, use X[10:11,:]. (Note that 10:11 does not include 11).

import numpy as np
X = np.zeros((100,10))
X.shape        # >> (100, 10)
xslice = X[10:11,:]
xslice.shape   # >> (1,10)

This makes it easy to understand with more dimensions too, no None juggling and figuring out which axis to use which index. Also no need to do extra bookkeeping regarding array size, just i:i+1 for any i that you would have used in regular indexing.

b = np.ones((2, 3, 4))
b.shape # >> (2, 3, 4)
b[1:2,:,:].shape  # >> (1, 3, 4)
b[:, 2:3, :].shape .  # >> (2, 1, 4)

回答 4

要添加涉及由gnebehay 按列表或数组建立索引的解决方案,还可以使用元组:

X[(10,),:]

To add to the solution involving indexing by lists or arrays by gnebehay, it is also possible to use tuples:

X[(10,),:]

回答 5

如果您在运行时按长度可能为1的数组建立索引,这将特别令人讨厌。对于这种情况,有np.ix_

some_array[np.ix_(row_index,column_index)]

This is especially annoying if you’re indexing by an array that might be length 1 at runtime. For that case, there’s np.ix_:

some_array[np.ix_(row_index,column_index)]

在Python中如何用numpy处理自然日志(例如“ ln()”)?

问题:在Python中如何用numpy处理自然日志(例如“ ln()”)?

使用numpy,如何执行以下操作:

ln(x)

它等效于:

np.log(x)

我这样一个看似微不足道的问题道歉,但我之间的差异的理解logln被认为ln是LOGSPACEè?

Using numpy, how can I do the following:

ln(x)

Is it equivalent to:

np.log(x)

I apologise for such a seemingly trivial question, but my understanding of the difference between log and ln is that ln is logspace e?


回答 0


回答 1

正确,np.log(x)是的自然日志(基本e日志)x

对于其他基准,请记住该日志定律:log-b(x) = log-k(x) / log-k(b)log-b任意任意基准中b,log是哪里,log-k在基准中是log k,例如

这里k = e

l = np.log(x) / np.log(100)

并且l是x的log-base-100

Correct, np.log(x) is the Natural Log (base e log) of x.

For other bases, remember this law of logs: log-b(x) = log-k(x) / log-k(b) where log-b is the log in some arbitrary base b, and log-k is the log in base k, e.g.

here k = e

l = np.log(x) / np.log(100)

and l is the log-base-100 of x


回答 2

我通常这样做:

from numpy import log as ln

也许这可以使您更舒适。

I usually do like this:

from numpy import log as ln

Perhaps this can make you more comfortable.


回答 3

您可以简单地通过将日志的底数设为e来进行相反的操作。

import math

e = 2.718281

math.log(e, 10) = 2.302585093
ln(10) = 2.30258093

You could simple just do the reverse by making the base of log to e.

import math

e = 2.718281

math.log(e, 10) = 2.302585093
ln(10) = 2.30258093

回答 4

from numpy.lib.scimath import logn
from math import e

#using: x - var
logn(e, x)
from numpy.lib.scimath import logn
from math import e

#using: x - var
logn(e, x)

在Python中绘制快速傅立叶变换

问题:在Python中绘制快速傅立叶变换

我可以访问NumPy和SciPy,并希望为数据集创建一个简单的FFT。我有两个列表,一个是y值,另一个是这些y值的时间戳。

将这些列表输入SciPy或NumPy方法并绘制所得FFT的最简单方法是什么?

我查看了示例,但是它们都依赖于创建具有一定数量的数据点和频率等的伪造数据集,而并没有真正展示如何仅使用一组数据和相应的时间戳来做到这一点。 。

我尝试了以下示例:

from scipy.fftpack import fft

# Number of samplepoints
N = 600

# Sample spacing
T = 1.0 / 800.0
x = np.linspace(0.0, N*T, N)
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)
yf = fft(y)
xf = np.linspace(0.0, 1.0/(2.0*T), N/2)
import matplotlib.pyplot as plt
plt.plot(xf, 2.0/N * np.abs(yf[0:N/2]))
plt.grid()
plt.show()

但是,当我更改fft数据集的参数并将其绘制成图时,会得到极其奇怪的结果,而且看起来频率的比例可能不正确。我不确定

这是我尝试FFT的数据的粘贴框

http://pastebin.com/0WhjjMkb http://pastebin.com/ksM4FvZS

当我fft()在整个事情上使用时,它的峰值只有零,而没有别的。

这是我的代码:

## Perform FFT with SciPy
signalFFT = fft(yInterp)

## Get power spectral density
signalPSD = np.abs(signalFFT) ** 2

## Get frequencies corresponding to signal PSD
fftFreq = fftfreq(len(signalPSD), spacing)

## Get positive half of frequencies
i = fftfreq>0

##
plt.figurefigsize = (8, 4));
plt.plot(fftFreq[i], 10*np.log10(signalPSD[i]));
#plt.xlim(0, 100);
plt.xlabel('Frequency [Hz]');
plt.ylabel('PSD [dB]')

间距等于xInterp[1]-xInterp[0]

I have access to NumPy and SciPy and want to create a simple FFT of a data set. I have two lists, one that is y values and the other is timestamps for those y values.

What is the simplest way to feed these lists into a SciPy or NumPy method and plot the resulting FFT?

I have looked up examples, but they all rely on creating a set of fake data with some certain number of data points, and frequency, etc. and don’t really show how to do it with just a set of data and the corresponding timestamps.

I have tried the following example:

from scipy.fftpack import fft

# Number of samplepoints
N = 600

# Sample spacing
T = 1.0 / 800.0
x = np.linspace(0.0, N*T, N)
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)
yf = fft(y)
xf = np.linspace(0.0, 1.0/(2.0*T), N/2)
import matplotlib.pyplot as plt
plt.plot(xf, 2.0/N * np.abs(yf[0:N/2]))
plt.grid()
plt.show()

But when I change the argument of fft to my data set and plot it, I get extremely odd results, and it appears the scaling for the frequency may be off. I am unsure.

Here is a pastebin of the data I am attempting to FFT

http://pastebin.com/0WhjjMkb http://pastebin.com/ksM4FvZS

When I use fft() on the whole thing it just has a huge spike at zero and nothing else.

Here is my code:

## Perform FFT with SciPy
signalFFT = fft(yInterp)

## Get power spectral density
signalPSD = np.abs(signalFFT) ** 2

## Get frequencies corresponding to signal PSD
fftFreq = fftfreq(len(signalPSD), spacing)

## Get positive half of frequencies
i = fftfreq>0

##
plt.figurefigsize = (8, 4));
plt.plot(fftFreq[i], 10*np.log10(signalPSD[i]));
#plt.xlim(0, 100);
plt.xlabel('Frequency [Hz]');
plt.ylabel('PSD [dB]')

Spacing is just equal to xInterp[1]-xInterp[0].


回答 0

因此,我在IPython笔记本中运行了功能等效的代码形式:

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack

# Number of samplepoints
N = 600
# sample spacing
T = 1.0 / 800.0
x = np.linspace(0.0, N*T, N)
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)
yf = scipy.fftpack.fft(y)
xf = np.linspace(0.0, 1.0/(2.0*T), N/2)

fig, ax = plt.subplots()
ax.plot(xf, 2.0/N * np.abs(yf[:N//2]))
plt.show()

我得到了我认为非常合理的输出。

在此处输入图片说明

自从我在工学院开始考虑信号处理以来,这个时间就比我想承认的要长,但是峰值在50和80正是我所期望的。那是什么问题呢?

响应发布的原始数据和评论

这里的问题是您没有定期数据。您应该始终检查输入任何算法的数据,以确保它是适当的。

import pandas
import matplotlib.pyplot as plt
#import seaborn
%matplotlib inline

# the OP's data
x = pandas.read_csv('http://pastebin.com/raw.php?i=ksM4FvZS', skiprows=2, header=None).values
y = pandas.read_csv('http://pastebin.com/raw.php?i=0WhjjMkb', skiprows=2, header=None).values
fig, ax = plt.subplots()
ax.plot(x, y)

在此处输入图片说明

So I run a functionally equivalent form of your code in an IPython notebook:

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack

# Number of samplepoints
N = 600
# sample spacing
T = 1.0 / 800.0
x = np.linspace(0.0, N*T, N)
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)
yf = scipy.fftpack.fft(y)
xf = np.linspace(0.0, 1.0/(2.0*T), N/2)

fig, ax = plt.subplots()
ax.plot(xf, 2.0/N * np.abs(yf[:N//2]))
plt.show()

I get what I believe to be very reasonable output.

enter image description here

It’s been longer than I care to admit since I was in engineering school thinking about signal processing, but spikes at 50 and 80 are exactly what I would expect. So what’s the issue?

In response to the raw data and comments being posted

The problem here is that you don’t have periodic data. You should always inspect the data that you feed into any algorithm to make sure that it’s appropriate.

import pandas
import matplotlib.pyplot as plt
#import seaborn
%matplotlib inline

# the OP's data
x = pandas.read_csv('http://pastebin.com/raw.php?i=ksM4FvZS', skiprows=2, header=None).values
y = pandas.read_csv('http://pastebin.com/raw.php?i=0WhjjMkb', skiprows=2, header=None).values
fig, ax = plt.subplots()
ax.plot(x, y)

enter image description here


回答 1

关于fft的重要一点是,它只能应用于时间戳统一的数据(时间上的统一采样,如上面所示)。

如果采样不均匀,请使用函数拟合数据。有几种教程和功能可供选择:

https://github.com/tiagopereira/python_tips/wiki/Scipy%3A-curve-fitting http://docs.scipy.org/doc/numpy/reference/generation/numpy.polyfit.html

如果无法选择拟合,则可以直接使用某种形式的插值将数据插值为统一采样:

https://docs.scipy.org/doc/scipy-0.14.0/reference/tutorial/interpolate.html

当您有统一的样本时,您只需要担心t[1] - t[0]样本的时间增量()。在这种情况下,您可以直接使用fft函数

Y    = numpy.fft.fft(y)
freq = numpy.fft.fftfreq(len(y), t[1] - t[0])

pylab.figure()
pylab.plot( freq, numpy.abs(Y) )
pylab.figure()
pylab.plot(freq, numpy.angle(Y) )
pylab.show()

这应该可以解决您的问题。

The important thing about fft is that it can only be applied to data in which the timestamp is uniform (i.e. uniform sampling in time, like what you have shown above).

In case of non-uniform sampling, please use a function for fitting the data. There are several tutorials and functions to choose from:

https://github.com/tiagopereira/python_tips/wiki/Scipy%3A-curve-fitting http://docs.scipy.org/doc/numpy/reference/generated/numpy.polyfit.html

If fitting is not an option, you can directly use some form of interpolation to interpolate data to a uniform sampling:

https://docs.scipy.org/doc/scipy-0.14.0/reference/tutorial/interpolate.html

When you have uniform samples, you will only have to wory about the time delta (t[1] - t[0]) of your samples. In this case, you can directly use the fft functions

Y    = numpy.fft.fft(y)
freq = numpy.fft.fftfreq(len(y), t[1] - t[0])

pylab.figure()
pylab.plot( freq, numpy.abs(Y) )
pylab.figure()
pylab.plot(freq, numpy.angle(Y) )
pylab.show()

This should solve your problem.


回答 2

您的高尖峰信号是由于信号的DC(不变,即freq = 0)部分引起的。这是规模问题。如果要查看非DC频率内容,则为了可视化,可能需要从偏移量1而不是信号FFT的偏移量0绘制。

修改@PaulH上面给出的示例

import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack

# Number of samplepoints
N = 600
# sample spacing
T = 1.0 / 800.0
x = np.linspace(0.0, N*T, N)
y = 10 + np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)
yf = scipy.fftpack.fft(y)
xf = np.linspace(0.0, 1.0/(2.0*T), N/2)

plt.subplot(2, 1, 1)
plt.plot(xf, 2.0/N * np.abs(yf[0:N/2]))
plt.subplot(2, 1, 2)
plt.plot(xf[1:], 2.0/N * np.abs(yf[0:N/2])[1:])

输出图: 用DC绘制FFT信号,然后将其删除(跳过频率= 0)

另一种方法是以对数刻度可视化数据:

使用:

plt.semilogy(xf, 2.0/N * np.abs(yf[0:N/2]))

将会呈现: 在此处输入图片说明

The high spike that you have is due to the DC (non-varying, i.e. freq = 0) portion of your signal. It’s an issue of scale. If you want to see non-DC frequency content, for visualization, you may need to plot from the offset 1 not from offset 0 of the FFT of the signal.

Modifying the example given above by @PaulH

import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack

# Number of samplepoints
N = 600
# sample spacing
T = 1.0 / 800.0
x = np.linspace(0.0, N*T, N)
y = 10 + np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)
yf = scipy.fftpack.fft(y)
xf = np.linspace(0.0, 1.0/(2.0*T), N/2)

plt.subplot(2, 1, 1)
plt.plot(xf, 2.0/N * np.abs(yf[0:N/2]))
plt.subplot(2, 1, 2)
plt.plot(xf[1:], 2.0/N * np.abs(yf[0:N/2])[1:])

The output plots: Ploting FFT signal with DC and then when removing it (skipping freq = 0)

Another way, is to visualize the data in log scale:

Using:

plt.semilogy(xf, 2.0/N * np.abs(yf[0:N/2]))

Will show: enter image description here


回答 3

作为对已经给出的答案的补充,我想指出的是,经常需要考虑FFT的bin大小。测试一堆值并选择对您的应用程序更有意义的值将是有意义的。通常,它与样本数量的大小相同。给出的大多数答案都假定了这一点,并且产生了很好且合理的结果。如果有人想探索一下,这是我的代码版本:

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack

fig = plt.figure(figsize=[14,4])
N = 600           # Number of samplepoints
Fs = 800.0
T = 1.0 / Fs      # N_samps*T (#samples x sample period) is the sample spacing.
N_fft = 80        # Number of bins (chooses granularity)
x = np.linspace(0, N*T, N)     # the interval
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)   # the signal

# removing the mean of the signal
mean_removed = np.ones_like(y)*np.mean(y)
y = y - mean_removed

# Compute the fft.
yf = scipy.fftpack.fft(y,n=N_fft)
xf = np.arange(0,Fs,Fs/N_fft)

##### Plot the fft #####
ax = plt.subplot(121)
pt, = ax.plot(xf,np.abs(yf), lw=2.0, c='b')
p = plt.Rectangle((Fs/2, 0), Fs/2, ax.get_ylim()[1], facecolor="grey", fill=True, alpha=0.75, hatch="/", zorder=3)
ax.add_patch(p)
ax.set_xlim((ax.get_xlim()[0],Fs))
ax.set_title('FFT', fontsize= 16, fontweight="bold")
ax.set_ylabel('FFT magnitude (power)')
ax.set_xlabel('Frequency (Hz)')
plt.legend((p,), ('mirrowed',))
ax.grid()

##### Close up on the graph of fft#######
# This is the same histogram above, but truncated at the max frequence + an offset. 
offset = 1    # just to help the visualization. Nothing important.
ax2 = fig.add_subplot(122)
ax2.plot(xf,np.abs(yf), lw=2.0, c='b')
ax2.set_xticks(xf)
ax2.set_xlim(-1,int(Fs/6)+offset)
ax2.set_title('FFT close-up', fontsize= 16, fontweight="bold")
ax2.set_ylabel('FFT magnitude (power) - log')
ax2.set_xlabel('Frequency (Hz)')
ax2.hold(True)
ax2.grid()

plt.yscale('log')

输出图: 在此处输入图片说明

Just as a complement to the answers already given, I would like to point out that often it is important to play with the size of the bins for the FFT. It would make sense to test a bunch of values and pick the one that makes more sense to your application. Often, it is in the same magnitude of the number of samples. This was as assumed by most of the answers given, and produces great and reasonable results. In case one wants to explore that, here is my code version:

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack

fig = plt.figure(figsize=[14,4])
N = 600           # Number of samplepoints
Fs = 800.0
T = 1.0 / Fs      # N_samps*T (#samples x sample period) is the sample spacing.
N_fft = 80        # Number of bins (chooses granularity)
x = np.linspace(0, N*T, N)     # the interval
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)   # the signal

# removing the mean of the signal
mean_removed = np.ones_like(y)*np.mean(y)
y = y - mean_removed

# Compute the fft.
yf = scipy.fftpack.fft(y,n=N_fft)
xf = np.arange(0,Fs,Fs/N_fft)

##### Plot the fft #####
ax = plt.subplot(121)
pt, = ax.plot(xf,np.abs(yf), lw=2.0, c='b')
p = plt.Rectangle((Fs/2, 0), Fs/2, ax.get_ylim()[1], facecolor="grey", fill=True, alpha=0.75, hatch="/", zorder=3)
ax.add_patch(p)
ax.set_xlim((ax.get_xlim()[0],Fs))
ax.set_title('FFT', fontsize= 16, fontweight="bold")
ax.set_ylabel('FFT magnitude (power)')
ax.set_xlabel('Frequency (Hz)')
plt.legend((p,), ('mirrowed',))
ax.grid()

##### Close up on the graph of fft#######
# This is the same histogram above, but truncated at the max frequence + an offset. 
offset = 1    # just to help the visualization. Nothing important.
ax2 = fig.add_subplot(122)
ax2.plot(xf,np.abs(yf), lw=2.0, c='b')
ax2.set_xticks(xf)
ax2.set_xlim(-1,int(Fs/6)+offset)
ax2.set_title('FFT close-up', fontsize= 16, fontweight="bold")
ax2.set_ylabel('FFT magnitude (power) - log')
ax2.set_xlabel('Frequency (Hz)')
ax2.hold(True)
ax2.grid()

plt.yscale('log')

the output plots: enter image description here


回答 4

我建立了一个函数,用于绘制真实信号的FFT。与先前的答案相比,我的功能额外的好处是您可以获得信号的实际幅度。

另外,由于假设是真实信号,因此FFT是对称的,因此我们只能绘制x轴的正向:

import matplotlib.pyplot as plt
import numpy as np
import warnings


def fftPlot(sig, dt=None, plot=True):
    # Here it's assumes analytic signal (real signal...) - so only half of the axis is required

    if dt is None:
        dt = 1
        t = np.arange(0, sig.shape[-1])
        xLabel = 'samples'
    else:
        t = np.arange(0, sig.shape[-1]) * dt
        xLabel = 'freq [Hz]'

    if sig.shape[0] % 2 != 0:
        warnings.warn("signal preferred to be even in size, autoFixing it...")
        t = t[0:-1]
        sig = sig[0:-1]

    sigFFT = np.fft.fft(sig) / t.shape[0]  # Divided by size t for coherent magnitude

    freq = np.fft.fftfreq(t.shape[0], d=dt)

    # Plot analytic signal - right half of frequence axis needed only...
    firstNegInd = np.argmax(freq < 0)
    freqAxisPos = freq[0:firstNegInd]
    sigFFTPos = 2 * sigFFT[0:firstNegInd]  # *2 because of magnitude of analytic signal

    if plot:
        plt.figure()
        plt.plot(freqAxisPos, np.abs(sigFFTPos))
        plt.xlabel(xLabel)
        plt.ylabel('mag')
        plt.title('Analytic FFT plot')
        plt.show()

    return sigFFTPos, freqAxisPos


if __name__ == "__main__":
    dt = 1 / 1000

    # Build a signal within Nyquist - the result will be the positive FFT with actual magnitude
    f0 = 200  # [Hz]
    t = np.arange(0, 1 + dt, dt)
    sig = 1 * np.sin(2 * np.pi * f0 * t) + \
        10 * np.sin(2 * np.pi * f0 / 2 * t) + \
        3 * np.sin(2 * np.pi * f0 / 4 * t) +\
        7.5 * np.sin(2 * np.pi * f0 / 5 * t)

    # Result in frequencies
    fftPlot(sig, dt=dt)
    # Result in samples (if the frequencies axis is unknown)
    fftPlot(sig)

解析FFT图结果

I’ve built a function that deals with plotting FFT of real signals. The extra bonus in my function relative to the messages above is that you get the ACTUAL amplitude of the signal. Also, because of the assumption of a real signal, the FFT is symmetric so we can plot only the positive side of the x axis:

import matplotlib.pyplot as plt
import numpy as np
import warnings


def fftPlot(sig, dt=None, plot=True):
    # here it's assumes analytic signal (real signal...)- so only half of the axis is required

    if dt is None:
        dt = 1
        t = np.arange(0, sig.shape[-1])
        xLabel = 'samples'
    else:
        t = np.arange(0, sig.shape[-1]) * dt
        xLabel = 'freq [Hz]'

    if sig.shape[0] % 2 != 0:
        warnings.warn("signal prefered to be even in size, autoFixing it...")
        t = t[0:-1]
        sig = sig[0:-1]

    sigFFT = np.fft.fft(sig) / t.shape[0]  # divided by size t for coherent magnitude

    freq = np.fft.fftfreq(t.shape[0], d=dt)

    # plot analytic signal - right half of freq axis needed only...
    firstNegInd = np.argmax(freq < 0)
    freqAxisPos = freq[0:firstNegInd]
    sigFFTPos = 2 * sigFFT[0:firstNegInd]  # *2 because of magnitude of analytic signal

    if plot:
        plt.figure()
        plt.plot(freqAxisPos, np.abs(sigFFTPos))
        plt.xlabel(xLabel)
        plt.ylabel('mag')
        plt.title('Analytic FFT plot')
        plt.show()

    return sigFFTPos, freqAxisPos


if __name__ == "__main__":
    dt = 1 / 1000

    # build a signal within nyquist - the result will be the positive FFT with actual magnitude
    f0 = 200  # [Hz]
    t = np.arange(0, 1 + dt, dt)
    sig = 1 * np.sin(2 * np.pi * f0 * t) + \
        10 * np.sin(2 * np.pi * f0 / 2 * t) + \
        3 * np.sin(2 * np.pi * f0 / 4 * t) +\
        7.5 * np.sin(2 * np.pi * f0 / 5 * t)

    # res in freqs
    fftPlot(sig, dt=dt)
    # res in samples (if freqs axis is unknown)
    fftPlot(sig)

analytic FFT plot result


回答 5

此页面上已经有不错的解决方案,但是所有人都假定数据集是统一/均匀采样/分布的。我将尝试提供一个更一般的随机采样数据示例。我还将使用此MATLAB教程作为示例:

添加所需的模块:

import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack
import scipy.signal

生成样本数据:

N = 600 # Number of samples
t = np.random.uniform(0.0, 1.0, N) # Assuming the time start is 0.0 and time end is 1.0
S = 1.0 * np.sin(50.0 * 2 * np.pi * t) + 0.5 * np.sin(80.0 * 2 * np.pi * t)
X = S + 0.01 * np.random.randn(N) # Adding noise

排序数据集:

order = np.argsort(t)
ts = np.array(t)[order]
Xs = np.array(X)[order]

重采样:

T = (t.max() - t.min()) / N # Average period
Fs = 1 / T # Average sample rate frequency
f = Fs * np.arange(0, N // 2 + 1) / N; # Resampled frequency vector
X_new, t_new = scipy.signal.resample(Xs, N, ts)

绘制数据和重新采样的数据:

plt.xlim(0, 0.1)
plt.plot(t_new, X_new, label="resampled")
plt.plot(ts, Xs, label="org")
plt.legend()
plt.ylabel("X")
plt.xlabel("t")

在此处输入图片说明

现在计算FFT:

Y = scipy.fftpack.fft(X_new)
P2 = np.abs(Y / N)
P1 = P2[0 : N // 2 + 1]
P1[1 : -2] = 2 * P1[1 : -2]

plt.ylabel("Y")
plt.xlabel("f")
plt.plot(f, P1)

在此处输入图片说明

PS我终于有时间实施一个更规范的算法来获得不均匀分布数据的傅立叶变换。您可以在此处查看代码,说明和示例Jupyter笔记本。

There are already great solutions on this page, but all have assumed the dataset is uniformly/evenly sampled/distributed. I will try to provide a more general example of randomly sampled data. I will also use this MATLAB tutorial as an example:

Adding the required modules:

import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack
import scipy.signal

Generating sample data:

N = 600 # number of samples
t = np.random.uniform(0.0, 1.0, N) # assuming the time start is 0.0 and time end is 1.0
S = 1.0 * np.sin(50.0 * 2 * np.pi * t) + 0.5 * np.sin(80.0 * 2 * np.pi * t) 
X = S + 0.01 * np.random.randn(N) # adding noise

Sorting the data set:

order = np.argsort(t)
ts = np.array(t)[order]
Xs = np.array(X)[order]

Resampling:

T = (t.max() - t.min()) / N # average period 
Fs = 1 / T # average sample rate frequency
f = Fs * np.arange(0, N // 2 + 1) / N; # resampled frequency vector
X_new, t_new = scipy.signal.resample(Xs, N, ts)

plotting the data and resampled data:

plt.xlim(0, 0.1)
plt.plot(t_new, X_new, label="resampled")
plt.plot(ts, Xs, label="org")
plt.legend()
plt.ylabel("X")
plt.xlabel("t")

enter image description here

now calculating the fft:

Y = scipy.fftpack.fft(X_new)
P2 = np.abs(Y / N)
P1 = P2[0 : N // 2 + 1]
P1[1 : -2] = 2 * P1[1 : -2]

plt.ylabel("Y")
plt.xlabel("f")
plt.plot(f, P1)

enter image description here

P.S. I finally got time to implement a more canonical algorithm to get a Fourier transform of unevenly distributed data. You may see the code, description, and example Jupyter notebook here.


回答 6

我写了这个额外的答案,以解释使用FFT时尖峰扩散的根源,特别是讨论scipy.fftpack教程,我在某些时候对此表示不同意。

在此示例中,记录时间tmax=N*T=0.75。信号是sin(50*2*pi*x) + 0.5*sin(80*2*pi*x)。频率信号应包含两个尖峰,其频率5080幅度分别为10.5。但是,如果所分析的信号没有整数周期,则由于信号的截断会出现扩散:

  • 派克1:50*tmax=37.5=>频率50不是的倍数1/tmax=>存在扩散的由于在该频率信号截断。
  • 派克2:80*tmax=60=>频率80是的倍数1/tmax=>无扩散由于在该频率信号截断。

这是一段代码,它分析的信号与教程(sin(50*2*pi*x) + 0.5*sin(80*2*pi*x))中的信号相同,但略有不同:

  1. 原始的scipy.fftpack示例。
  2. 原始scipy.fftpack示例具有整数个信号周期(tmax=1.0而不是0.75为了避免截断扩散)。
  3. 原始scipy.fftpack示例,其中包含整数个信号周期,并且日期和频率均取自FFT理论。

编码:

import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack

# 1. Linspace
N = 600
# Sample spacing
tmax = 3/4
T = tmax / N # =1.0 / 800.0
x1 = np.linspace(0.0, N*T, N)
y1 = np.sin(50.0 * 2.0*np.pi*x1) + 0.5*np.sin(80.0 * 2.0*np.pi*x1)
yf1 = scipy.fftpack.fft(y1)
xf1 = np.linspace(0.0, 1.0/(2.0*T), N//2)

# 2. Integer number of periods
tmax = 1
T = tmax / N # Sample spacing
x2 = np.linspace(0.0, N*T, N)
y2 = np.sin(50.0 * 2.0*np.pi*x2) + 0.5*np.sin(80.0 * 2.0*np.pi*x2)
yf2 = scipy.fftpack.fft(y2)
xf2 = np.linspace(0.0, 1.0/(2.0*T), N//2)

# 3. Correct positioning of dates relatively to FFT theory ('arange' instead of 'linspace')
tmax = 1
T = tmax / N # Sample spacing
x3 = T * np.arange(N)
y3 = np.sin(50.0 * 2.0*np.pi*x3) + 0.5*np.sin(80.0 * 2.0*np.pi*x3)
yf3 = scipy.fftpack.fft(y3)
xf3 = 1/(N*T) * np.arange(N)[:N//2]

fig, ax = plt.subplots()
# Plotting only the left part of the spectrum to not show aliasing
ax.plot(xf1, 2.0/N * np.abs(yf1[:N//2]), label='fftpack tutorial')
ax.plot(xf2, 2.0/N * np.abs(yf2[:N//2]), label='Integer number of periods')
ax.plot(xf3, 2.0/N * np.abs(yf3[:N//2]), label='Correct positioning of dates')
plt.legend()
plt.grid()
plt.show()

输出:

就像这里可能的那样,即使使用整数周期,仍然会有一些扩散。此行为是由于scipy.fftpack教程中日期和频率的位置不正确造成的。因此,在离散傅立叶变换理论中:

  • 信号应t=0,T,...,(N-1)*T在T为采样周期且信号总持续时间为的日期进行评估tmax=N*T。请注意,我们在停下来tmax-T
  • 相关联的频率f=0,df,...,(N-1)*df,其中df=1/tmax=1/(N*T)是采样频率。信号的所有谐波应为采样频率的倍数,以避免扩散。

在上面的示例中,您可以看到使用arange代替linspace可以避免频谱中的额外扩散。此外,使用该linspace版本还会导致尖峰的偏移,该尖峰的频率略高于应有的尖峰,这在第一张图片中可以看到,在这些图片中,尖峰稍微位于频率50和的右边80

我将得出结论,用法示例应替换为以下代码(在我看来,这不太容易引起误解):

import numpy as np
from scipy.fftpack import fft

# Number of sample points
N = 600
T = 1.0 / 800.0
x = T*np.arange(N)
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)
yf = fft(y)
xf = 1/(N*T)*np.arange(N//2)
import matplotlib.pyplot as plt
plt.plot(xf, 2.0/N * np.abs(yf[0:N//2]))
plt.grid()
plt.show()

输出(第二个峰值不再扩散):

我认为此答案仍会带来一些有关如何正确应用离散傅立叶变换的附加说明。显然,我的答案太长了,总是有其他要说的东西(例如,关于“别名”的简短讨论,关于“窗口化”的说法很多),所以我将停止。

我认为,在应用离散傅里叶变换时,深刻理解离散傅里叶变换的原理非常重要,因为我们都知道很多人在应用离散傅里叶变换时都会在其中添加因数以获得自己想要的东西。

I write this additionnal answer to explain the origins of the diffusion of the spikes when using fft and especially discuss the scipy.fftpack tutorial with which I disagree at some point.

In this example, the recording time tmax=N*T=0.75. The signal is sin(50*2*pi*x)+0.5*sin(80*2*pi*x). The frequency signal should contain 2 spikes at frequencies 50 and 80 with amplitudes 1 and 0.5. However, if the analysed signal does not have a integer number of periods diffusion can appear due to the truncation of the signal:

  • Pike 1: 50*tmax=37.5 => frequency 50 is not a multiple of 1/tmax => Presence of diffusion due to signal truncation at this frequency.
  • Pike 2: 80*tmax=60 => frequency 80 is a multiple of 1/tmax => No diffusion due to signal truncation at this frequency.

Here is a code that analyses the same signal as in the tutorial (sin(50*2*pi*x)+0.5*sin(80*2*pi*x)) but with the slight differences:

  1. The original scipy.fftpack example.
  2. The original scipy.fftpack example with an integer number of signal periods (tmax=1.0 instead of 0.75 to avoid truncation diffusion).
  3. The original scipy.fftpack example with an integer number of signal periods and where the dates and frequencies are taken from the FFT theory.

The code:

import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack

# 1. Linspace
N = 600
# sample spacing
tmax = 3/4
T = tmax / N # =1.0 / 800.0
x1 = np.linspace(0.0, N*T, N)
y1 = np.sin(50.0 * 2.0*np.pi*x1) + 0.5*np.sin(80.0 * 2.0*np.pi*x1)
yf1 = scipy.fftpack.fft(y1)
xf1 = np.linspace(0.0, 1.0/(2.0*T), N//2)

# 2. Integer number of periods
tmax = 1
T = tmax / N # sample spacing
x2 = np.linspace(0.0, N*T, N)
y2 = np.sin(50.0 * 2.0*np.pi*x2) + 0.5*np.sin(80.0 * 2.0*np.pi*x2)
yf2 = scipy.fftpack.fft(y2)
xf2 = np.linspace(0.0, 1.0/(2.0*T), N//2)

# 3. Correct positionning of dates relatively to FFT theory (arange instead of linspace)
tmax = 1
T = tmax / N # sample spacing
x3 = T * np.arange(N)
y3 = np.sin(50.0 * 2.0*np.pi*x3) + 0.5*np.sin(80.0 * 2.0*np.pi*x3)
yf3 = scipy.fftpack.fft(y3)
xf3 = 1/(N*T) * np.arange(N)[:N//2]

fig, ax = plt.subplots()
# Plotting only the left part of the spectrum to not show aliasing
ax.plot(xf1, 2.0/N * np.abs(yf1[:N//2]), label='fftpack tutorial')
ax.plot(xf2, 2.0/N * np.abs(yf2[:N//2]), label='Integer number of periods')
ax.plot(xf3, 2.0/N * np.abs(yf3[:N//2]), label='Correct positionning of dates')
plt.legend()
plt.grid()
plt.show()

Output:

As it can be here, even with using an integer number of periods some diffusion still remains. This behaviour is due to a bad positionning of dates and frequencies in the scipy.fftpack tutorial. Hence, in the theory of discrete Fourier transforms:

  • the signal should be evaluated at dates t=0,T,...,(N-1)*T where T is the sampling period and the total duration of the signal is tmax=N*T. Note that we stop at tmax-T.
  • the associated frequencies are f=0,df,...,(N-1)*df where df=1/tmax=1/(N*T) is the sampling frequency. All harmonics of the signal should be multiple of the sampling frequency to avoid diffusion.

In the example above, you can see that the use of arange instead of linspace enables to avoid additional diffusion in the frequency spectrum. Moreover, using the linspace version also leads to an offset of the spikes that are located at slightly higher frequencies than what they should be as it can be seen in the first picture where the spikes are a little bit at the right of the frequencies 50 and 80.

I’ll just conclude that the example of usage should be replace by the following code (which is less misleading in my opinion):

import numpy as np
from scipy.fftpack import fft
# Number of sample points
N = 600
T = 1.0 / 800.0
x = T*np.arange(N)
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)
yf = fft(y)
xf = 1/(N*T)*np.arange(N//2)
import matplotlib.pyplot as plt
plt.plot(xf, 2.0/N * np.abs(yf[0:N//2]))
plt.grid()
plt.show()

Output (the second spike is not diffused anymore):

I think this answer still bring some additional explanations on how to apply correctly discrete Fourier transform. Obviously, my answer is too long and there is always additional things to say (@ewerlopes talked briefly about aliasing for instance and a lot can be said about windowing) so I’ll stop. I think that it is very important to understand deeply the principles of discrete Fourier transform when applying it because we all know so much people adding factors here and there when applying it in order to obtain what they want.


DataFrame中的字符串,但dtype是object

问题:DataFrame中的字符串,但dtype是object

为什么Pandas告诉我我有对象,尽管所选列中的每个项目都是一个字符串-即使经过显式转换也是如此。

这是我的DataFrame:

<class 'pandas.core.frame.DataFrame'>
Int64Index: 56992 entries, 0 to 56991
Data columns (total 7 columns):
id            56992  non-null values
attr1         56992  non-null values
attr2         56992  non-null values
attr3         56992  non-null values
attr4         56992  non-null values
attr5         56992  non-null values
attr6         56992  non-null values
dtypes: int64(2), object(5)

他们五个dtype object。我将这些对象明确转换为字符串:

for c in df.columns:
    if df[c].dtype == object:
        print "convert ", df[c].name, " to string"
        df[c] = df[c].astype(str)

然后,尽管显示,df["attr2"]仍然是正确的。dtype objecttype(df["attr2"].ix[0]str

熊猫区分int64float64object。没有时背后的逻辑是什么dtype str?为什么被str覆盖object

Why does Pandas tell me that I have objects, although every item in the selected column is a string — even after explicit conversion.

This is my DataFrame:

<class 'pandas.core.frame.DataFrame'>
Int64Index: 56992 entries, 0 to 56991
Data columns (total 7 columns):
id            56992  non-null values
attr1         56992  non-null values
attr2         56992  non-null values
attr3         56992  non-null values
attr4         56992  non-null values
attr5         56992  non-null values
attr6         56992  non-null values
dtypes: int64(2), object(5)

Five of them are dtype object. I explicitly convert those objects to strings:

for c in df.columns:
    if df[c].dtype == object:
        print "convert ", df[c].name, " to string"
        df[c] = df[c].astype(str)

Then, df["attr2"] still has dtype object, although type(df["attr2"].ix[0] reveals str, which is correct.

Pandas distinguishes between int64 and float64 and object. What is the logic behind it when there is no dtype str? Why is a str covered by object?


回答 0

dtype对象来自NumPy,它描述ndarray中元素的类型。ndarray中的每个元素都必须具有相同的字节大小。对于int64和float64,它们是8个字节。但是对于字符串,字符串的长度不是固定的。因此,熊猫没有直接将字符串的字节保存在ndarray中,而是使用对象ndarray来保存指向对象的指针,因此,这种ndarray的dtype是object。

这是一个例子:

  • int64数组包含4个int64值。
  • 对象数组包含4个指向3个字符串对象的指针。

在此处输入图片说明

The dtype object comes from NumPy, it describes the type of element in a ndarray. Every element in a ndarray must has the same size in byte. For int64 and float64, they are 8 bytes. But for strings, the length of the string is not fixed. So instead of save the bytes of strings in the ndarray directly, Pandas use object ndarray, which save pointers to objects, because of this the dtype of this kind ndarray is object.

Here is an example:

  • the int64 array contains 4 int64 value.
  • the object array contains 4 pointers to 3 string objects.

enter image description here


回答 1

接受的答案是好的。只是想提供一个参考文档的答案。该文档说:

熊猫使用对象dtype来存储字符串。

正如主要评论所说:“不用担心;它应该像这样。” (尽管可接受的答案在解释“为什么”方面做得很好,字符串是可变长度的)

但是对于字符串,字符串的长度不是固定的。

The accepted answer is good. Just wanted to provide an answer which referenced the documentation. The documentation says:

Pandas uses the object dtype for storing strings.

As the leading comment says “Don’t worry about it; it’s supposed to be like this.” (Although the accepted answer did a great job explaining the “why”; strings are variable-length)

But for strings, the length of the string is not fixed.


回答 2

@HYRY的答案很好。我只想提供更多背景信息。

阵列存储的数据作为连续的固定大小的存储器块。这些属性的结合使阵列可以快速进行数据访问。例如,考虑您的计算机可能如何存储32位整数数组[3,0,1]

在此处输入图片说明

如果您要求计算机获取数组中的第3个元素,它将从头开始,然后跨64位跳转到第3个元素。确切知道要跳过多少位才可以使数组快速运行

现在考虑字符串的顺序['hello', 'i', 'am', 'a', 'banana']。字符串是大小不同的对象,因此,如果您尝试将它们存储在连续的内存块中,它将最终看起来像这样。

在此处输入图片说明

现在,您的计算机没有快速的方法来访问随机请求的元素。克服这个问题的关键是使用指针。基本上,将每个字符串存储在某个随机的内存位置,然后用每个字符串的内存地址填充数组。(内存地址只是整数。)所以现在,事情看起来像这样

在此处输入图片说明

现在,如果您像以前一样要求计算机获取第三个元素,它可以跨64位跳转(假设内存地址是32位整数),然后再执行一个步骤来获取字符串。

NumPy面临的挑战是不能保证指针实际上指向字符串。这就是为什么它将dtype报告为“对象”的原因。

无耻地插入我自己的博客文章,最初是在此进行讨论的。

@HYRY’s answer is great. I just want to provide a little more context..

Arrays store data as contiguous, fixed-size memory blocks. The combination of these properties together is what makes arrays lightning fast for data access. For example, consider how your computer might store an array of 32-bit integers, [3,0,1].

enter image description here

If you ask your computer to fetch the 3rd element in the array, it’ll start at the beginning and then jump across 64 bits to get to the 3rd element. Knowing exactly how many bits to jump across is what makes arrays fast.

Now consider the sequence of strings ['hello', 'i', 'am', 'a', 'banana']. Strings are objects that vary in size, so if you tried to store them in contiguous memory blocks, it’d end up looking like this.

enter image description here

Now your computer doesn’t have a fast way to access a randomly requested element. The key to overcoming this is to use pointers. Basically, store each string in some random memory location, and fill the array with the memory address of each string. (Memory addresses are just integers.) So now, things look like this

enter image description here

Now, if you ask your computer to fetch the 3rd element, just as before, it can jump across 64 bits (assuming the memory addresses are 32-bit integers) and then make one extra step to go fetch the string.

The challenge for NumPy is that there’s no guarantee the pointers are actually pointing to strings. That’s why it reports the dtype as ‘object’.

Shamelessly gonna plug my own blog article where I originally discussed this.


回答 3

从1.0.0版开始(2020年1月),pandas作为实验功能被引入,它通过提供对字符串类型的一流支持pandas.StringDtype

虽然您仍然会object默认看到,但是可以通过指定dtypeof pd.StringDtype或简单地使用新类型'string'

>>> pd.Series(['abc', None, 'def'])
0     abc
1    None
2     def
dtype: object
>>> pd.Series(['abc', None, 'def'], dtype=pd.StringDtype())
0     abc
1    <NA>
2     def
dtype: string
>>> pd.Series(['abc', None, 'def']).astype('string')
0     abc
1    <NA>
2     def
dtype: string

As of version 1.0.0 (January 2020), pandas has introduced as an experimental feature providing first-class support for string types through pandas.StringDtype.

While you’ll still be seeing object by default, the new type can be used by specifying a dtype of pd.StringDtype or simply 'string':

>>> pd.Series(['abc', None, 'def'])
0     abc
1    None
2     def
dtype: object
>>> pd.Series(['abc', None, 'def'], dtype=pd.StringDtype())
0     abc
1    <NA>
2     def
dtype: string
>>> pd.Series(['abc', None, 'def']).astype('string')
0     abc
1    <NA>
2     def
dtype: string

imshow()的数字太小

问题:imshow()的数字太小

我正在尝试使用imshow()可视化一个numpy数组,因为它类似于Matlab中的imagesc()。

imshow(random.rand(8, 90), interpolation='nearest')

最终的图形在灰色窗口的中心很小,而大部分空间都未被占用。如何设置参数以使图形更大?我尝试了figsize =(xx,xx),这不是我想要的。谢谢!

I’m trying to visualize a numpy array using imshow() since it’s similar to imagesc() in Matlab.

imshow(random.rand(8, 90), interpolation='nearest')

The resulting figure is very small at the center of the grey window, while most of the space is unoccupied. How can I set the parameters to make the figure larger? I tried figsize=(xx,xx) and it’s not what I want. Thanks!


回答 0

如果你不给一个aspect参数imshow,它会使用值image.aspect在你的matplotlibrc。在一个新的该值的默认值matplotlibrcequal。因此,imshow将以相等的纵横比绘制数组。

如果您不需要平等的方面,可以将其设置aspectauto

imshow(random.rand(8, 90), interpolation='nearest', aspect='auto')

如下图

imshow自动

如果您想要相等的长宽比,则必须figsize根据长宽比进行调整

fig, ax = subplots(figsize=(18, 2))
ax.imshow(random.rand(8, 90), interpolation='nearest')
tight_layout()

这给你:

不等式

If you don’t give an aspect argument to imshow, it will use the value for image.aspect in your matplotlibrc. The default for this value in a new matplotlibrc is equal. So imshow will plot your array with equal aspect ratio.

If you don’t need an equal aspect you can set aspect to auto

imshow(random.rand(8, 90), interpolation='nearest', aspect='auto')

which gives the following figure

imshow-auto

If you want an equal aspect ratio you have to adapt your figsize according to the aspect

fig, ax = subplots(figsize=(18, 2))
ax.imshow(random.rand(8, 90), interpolation='nearest')
tight_layout()

which gives you:

imshow-equal


回答 1

奇怪,它绝对对我有用:

from matplotlib import pyplot as plt

plt.figure(figsize = (20,2))
plt.imshow(random.rand(8, 90), interpolation='nearest')

我正在使用“ MacOSX”后端,顺便说一句。

That’s strange, it definitely works for me:

from matplotlib import pyplot as plt

plt.figure(figsize = (20,2))
plt.imshow(random.rand(8, 90), interpolation='nearest')

I am using the “MacOSX” backend, btw.


回答 2

我也是python的新手。这看起来像会做您想做的事

axes([0.08, 0.08, 0.94-0.08, 0.94-0.08]) #[left, bottom, width, height]
axis('scaled')`

我相信这决定了画布的大小。

I’m new to python too. Here is something that looks like will do what you want to

axes([0.08, 0.08, 0.94-0.08, 0.94-0.08]) #[left, bottom, width, height]
axis('scaled')`

I believe this decides the size of the canvas.


回答 3

更新2020

按照@baxxx的要求,这是一个更新,因为random.rand同时已弃用。

这适用于matplotlip 3.2.1:

from matplotlib import pyplot as plt
import random
import numpy as np

random = np.random.random ([8,90])

plt.figure(figsize = (20,2))
plt.imshow(random, interpolation='nearest')

此图:

在此处输入图片说明

要更改随机数,您可以进行实验np.random.normal(0,1,(8,90))(此处的均值= 0,标准差= 1)。

Update 2020

as requested by @baxxx, here is an update because random.rand is deprecated meanwhile.

This works with matplotlip 3.2.1:

from matplotlib import pyplot as plt
import random
import numpy as np

random = np.random.random ([8,90])

plt.figure(figsize = (20,2))
plt.imshow(random, interpolation='nearest')

This plots:

enter image description here

To change the random number, you can experiment with np.random.normal(0,1,(8,90)) (here mean = 0, standard deviation = 1).


将HDF5用于大型阵列存储(而不是平面二进制文件)是否具有分析速度或内存使用优势?

问题:将HDF5用于大型阵列存储(而不是平面二进制文件)是否具有分析速度或内存使用优势?

我正在处理大型3D阵列,通常需要以各种方式对其进行切片以进行各种数据分析。一个典型的“立方体”可以达到〜100GB(将来可能会更大)

似乎对于python中的大型数据集,通常推荐的文件格式是使用HDF5(h5py或pytables)。我的问题是:使用HDF5来存储和分析这些多维数据集,而不是将它们存储在简单的平面二进制文件中,对速度或内存使用有好处吗?HDF5是否更适合表格数据,而不是像我正在使用的大型数组?我看到HDF5可以提供很好的压缩,但是我对处理速度和处理内存溢出更感兴趣。

我经常只想分析多维数据集的一个大子集。pytables和h5py的一个缺点似乎是,当我对数组进行切片时,我总是得到一个numpy数组,占用了内存。但是,如果我对平面二进制文件的numpy内存映射进行切片,则可以获得一个视图,该视图将数据保留在磁盘上。因此,看来我可以更轻松地分析数据的特定扇区,而不会耗尽内存。

我已经浏览了pytables和h5py,到目前为止,还没有看到两者对我的好处。

I am processing large 3D arrays, which I often need to slice in various ways to do a variety of data analysis. A typical “cube” can be ~100GB (and will likely get larger in the future)

It seems that the typical recommended file format for large datasets in python is to use HDF5 (either h5py or pytables). My question is: is there any speed or memory usage benefit to using HDF5 to store and analyze these cubes over storing them in simple flat binary files? Is HDF5 more appropriate for tabular data, as opposed to large arrays like what I am working with? I see that HDF5 can provide nice compression, but I am more interested in processing speed and dealing with memory overflow.

I frequently want to analyze only one large subset of the cube. One drawback of both pytables and h5py is it seems is that when I take a slice of the array, I always get a numpy array back, using up memory. However, if I slice a numpy memmap of a flat binary file, I can get a view, which keeps the data on disk. So, it seems that I can more easily analyze specific sectors of my data without overrunning my memory.

I have explored both pytables and h5py, and haven’t seen the benefit of either so far for my purpose.


回答 0

HDF5的优势:组织,灵活性,互操作性

HDF5的一些主要优点是其层次结构(类似于文件夹/文件),与每个项目一起存储的可选任意元数据以及其灵活性(例如压缩)。这种组织结构和元数据存储听起来很琐碎,但在实践中非常有用。

HDF的另一个优点是,数据集可以是固定大小的灵活的尺寸。因此,将数据附加到大型数据集很容易,而无需创建整个新副本。

此外,HDF5是一种标准格式,几乎所有语言都可以使用库,因此使用HDF可以很容易地在Matlab,Fortran,R,C和Python之间共享磁盘数据。(公平地说,只要知道C与F的顺序并知道存储数组的形状,dtype等,使用大型二进制数组也不太困难。)

HDF对于大型阵列的优势:更快的任意切片I / O

就像TL / DR一样:对于约8GB的3D阵列,使用分块的HDF5数据集沿任何轴读取“完整”切片大约需要20秒,而对于3个小时(最坏情况),则需要0.3秒(最佳情况)相同数据的映射数组。

除了上面列出的内容外,“块状” *磁盘数据格式(例如HDF5)还有另一个大优势:读取任意片(强调任意)通常会更快,因为磁盘上的数据更连续。平均。

*(HDF5不必是分块的数据格式。它支持分块,但不是必需的。实际上h5py,如果我没记错的话,在其中创建数据集的默认设置是不分块。)

基本上,对于一个给定的数据集片,最佳情况下的磁盘读取速度和最坏情况下的磁盘读取速度将与分块的HDF数据集相当接近(假设您选择了一个合理的块大小,或者让一个库为您选择了一个)。用一个简单的二进制数组,最好的情况也比较快,但最坏的情况是糟糕。

请注意,如果您有SSD,则可能不会注意到读/写速度的巨大差异。但是,使用常规硬盘驱动器,顺序读取要比随机读取快得多。(即,普通硬盘驱动器需要很长的seek时间。)HDF在SSD上仍然具有优势,但更多的原因是其其他功能(例如,元数据,组织等)而不是原始速度。


首先,为了消除混乱,访问h5py数据集将返回一个对象,该对象的行为与numpy数组非常相似,但是直到将数据切片后才将其加载到内存中。(类似于memmap,但不完全相同。)有关更多信息,请参见h5py介绍

对数据集进行切片会将数据的一个子集加载到内存中,但是大概您想对它做点什么,这时无论如何您都将需要它在内存中。

如果您确实想进行核外计算,则可以使用pandas或轻松地获得表格数据pytables。可能h5py(大型ND阵列更合适),但是您需要降低到较低的水平并自己处理迭代。

但是,类似numpy的内核外计算的未来是Blaze。如果您真的想走那条路,请看一下


“无条件”案例

首先,考虑一个写入磁盘的3D C顺序数组(我将通过调用arr.ravel()和打印结果来模拟它,以使内容更加可见):

In [1]: import numpy as np

In [2]: arr = np.arange(4*6*6).reshape(4,6,6)

In [3]: arr
Out[3]:
array([[[  0,   1,   2,   3,   4,   5],
        [  6,   7,   8,   9,  10,  11],
        [ 12,  13,  14,  15,  16,  17],
        [ 18,  19,  20,  21,  22,  23],
        [ 24,  25,  26,  27,  28,  29],
        [ 30,  31,  32,  33,  34,  35]],

       [[ 36,  37,  38,  39,  40,  41],
        [ 42,  43,  44,  45,  46,  47],
        [ 48,  49,  50,  51,  52,  53],
        [ 54,  55,  56,  57,  58,  59],
        [ 60,  61,  62,  63,  64,  65],
        [ 66,  67,  68,  69,  70,  71]],

       [[ 72,  73,  74,  75,  76,  77],
        [ 78,  79,  80,  81,  82,  83],
        [ 84,  85,  86,  87,  88,  89],
        [ 90,  91,  92,  93,  94,  95],
        [ 96,  97,  98,  99, 100, 101],
        [102, 103, 104, 105, 106, 107]],

       [[108, 109, 110, 111, 112, 113],
        [114, 115, 116, 117, 118, 119],
        [120, 121, 122, 123, 124, 125],
        [126, 127, 128, 129, 130, 131],
        [132, 133, 134, 135, 136, 137],
        [138, 139, 140, 141, 142, 143]]])

这些值将按顺序存储在磁盘上,如下面的第4行所示。(暂时忽略文件系统详细信息和碎片。)

In [4]: arr.ravel(order='C')
Out[4]:
array([  0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,
        13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,
        26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,
        39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,
        52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,  64,
        65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,
        78,  79,  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,
        91,  92,  93,  94,  95,  96,  97,  98,  99, 100, 101, 102, 103,
       104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
       117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
       130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143])

在最佳情况下,让我们沿第一个轴剖一下。请注意,这些只是数组的前36个值。这将是一个非常快的阅读!(一寻一读)

In [5]: arr[0,:,:]
Out[5]:
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23],
       [24, 25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34, 35]])

同样,沿第一个轴的下一个切片将仅是下一个36个值。要沿该轴读取完整的切片,我们只需执行一项seek操作。如果我们要读取的只是沿着该轴的各个切片,那么这就是理想的文件结构。

但是,让我们考虑最坏的情况:沿着最后一个轴的切片。

In [6]: arr[:,:,0]
Out[6]:
array([[  0,   6,  12,  18,  24,  30],
       [ 36,  42,  48,  54,  60,  66],
       [ 72,  78,  84,  90,  96, 102],
       [108, 114, 120, 126, 132, 138]])

要读入此片,我们需要36次搜索和36次读取,因为所有值都在磁盘上分开。他们都不是相邻的!

这看起来似乎很小,但是随着我们使用越来越大的数组,操作的数量和大小seek迅速增长。对于以这种方式存储并通过via读入的大型(〜10Gb)3D阵列memmap,即使使用现代硬件,沿“最差”轴读取整个切片也很容易花费数十分钟。同时,沿最佳轴的切片可能会花费不到一秒钟的时间。为了简单起见,我只显示单个轴上的“完整”切片,但是对于数据的任何子集的任意切片,都会发生完全相同的事情。

顺便说一句,有几种文件格式可以利用此功能,并且基本上在磁盘上存储三份巨大的 3D阵列副本:一份以C顺序,一份以F顺序,一份在两者之间的中间位置。(一个示例是Geoprobe的D3D格式,尽管我不确定它是否在任何地方都有记录。)谁在乎最终文件大小是否为4TB,但存储空间却很便宜!疯狂的是,由于主要用例是在每个方向上提取单个子切片,因此您要进行的读取非常非常快。效果很好!


简单的“块状”案例

假设我们将3D数组的2x2x2“块”存储为磁盘上的连续块。换句话说,类似:

nx, ny, nz = arr.shape
slices = []
for i in range(0, nx, 2):
    for j in range(0, ny, 2):
        for k in range(0, nz, 2):
            slices.append((slice(i, i+2), slice(j, j+2), slice(k, k+2)))

chunked = np.hstack([arr[chunk].ravel() for chunk in slices])

因此磁盘上的数据如下所示chunked

array([  0,   1,   6,   7,  36,  37,  42,  43,   2,   3,   8,   9,  38,
        39,  44,  45,   4,   5,  10,  11,  40,  41,  46,  47,  12,  13,
        18,  19,  48,  49,  54,  55,  14,  15,  20,  21,  50,  51,  56,
        57,  16,  17,  22,  23,  52,  53,  58,  59,  24,  25,  30,  31,
        60,  61,  66,  67,  26,  27,  32,  33,  62,  63,  68,  69,  28,
        29,  34,  35,  64,  65,  70,  71,  72,  73,  78,  79, 108, 109,
       114, 115,  74,  75,  80,  81, 110, 111, 116, 117,  76,  77,  82,
        83, 112, 113, 118, 119,  84,  85,  90,  91, 120, 121, 126, 127,
        86,  87,  92,  93, 122, 123, 128, 129,  88,  89,  94,  95, 124,
       125, 130, 131,  96,  97, 102, 103, 132, 133, 138, 139,  98,  99,
       104, 105, 134, 135, 140, 141, 100, 101, 106, 107, 136, 137, 142, 143])

只是为了表明它们是的2x2x2块arr,请注意这些是的前8个值chunked

In [9]: arr[:2, :2, :2]
Out[9]:
array([[[ 0,  1],
        [ 6,  7]],

       [[36, 37],
        [42, 43]]])

要读取沿轴的任何切片,我们将读取6或9个连续的块(所需数据的两倍),然后仅保留所需的部分。在最坏的情况下,最大搜索数为9,而非分块版本的搜索数最大为36。(但是最好的情况仍然是6个搜索,而对于内存阵列则为1个。)由于顺序读取与搜索相比非常快,因此大大减少了将任意子集读入内存所需的时间。再次,这种效果随着更大的阵列而变得更大。

HDF5更进一步。块不必连续存储,它们由B树索引。此外,它们不必在磁盘上具有相同的大小,因此可以将压缩应用于每个块。


分块数组 h5py

默认情况下,h5py不会在磁盘上创建分块的HDF文件(pytables相比之下,我认为是)。chunks=True但是,如果在创建数据集时指定,则会在磁盘上获得分块数组。

作为一个简短的示例:

import numpy as np
import h5py

data = np.random.random((100, 100, 100))

with h5py.File('test.hdf', 'w') as outfile:
    dset = outfile.create_dataset('a_descriptive_name', data=data, chunks=True)
    dset.attrs['some key'] = 'Did you want some metadata?'

请注意,这会chunks=True告诉h5py我们自动选择块大小。如果您最了解最常用的用例,则可以通过指定形状元组来优化块的大小/形状(例如(2,2,2),在上面的简单示例中)。这使您可以更有效地沿特定轴进行读取,或针对特定大小的读取/写入进行优化。


I / O性能比较

为了强调这一点,让我们比较从分块的HDF5数据集和包含相同确切数据的大型(〜8GB)Fortran排序3D数组中读取的片段。

我已经清除了每次运行之间的所有操作系统缓存,因此我们看到的是“冷”性能。

对于每种文件类型,我们将测试沿第一个轴的“完整” x切片和沿最后一个轴的“完整” z切片读取。对于按Fortran排序的映射数组,“ x”切片是最坏的情况,而“ z”切片是最好的情况。

所使用的代码基于要点(包括创建hdf文件)。我无法轻松共享此处使用的数据,但是您可以通过形状相同的零数组(621, 4991, 2600)和type)来模拟它np.uint8

chunked_hdf.py如下所示:

import sys
import h5py

def main():
    data = read()

    if sys.argv[1] == 'x':
        x_slice(data)
    elif sys.argv[1] == 'z':
        z_slice(data)

def read():
    f = h5py.File('/tmp/test.hdf5', 'r')
    return f['seismic_volume']

def z_slice(data):
    return data[:,:,0]

def x_slice(data):
    return data[0,:,:]

main()

memmapped_array.py相似,但要确保将切片实际加载到内存中要复杂得多(默认情况下,memmapped将返回另一个数组,这不是苹果与苹果的比较)。

import numpy as np
import sys

def main():
    data = read()

    if sys.argv[1] == 'x':
        x_slice(data)
    elif sys.argv[1] == 'z':
        z_slice(data)

def read():
    big_binary_filename = '/data/nankai/data/Volumes/kumdep01_flipY.3dv.vol'
    shape = 621, 4991, 2600
    header_len = 3072

    data = np.memmap(filename=big_binary_filename, mode='r', offset=header_len,
                     order='F', shape=shape, dtype=np.uint8)
    return data

def z_slice(data):
    dat = np.empty(data.shape[:2], dtype=data.dtype)
    dat[:] = data[:,:,0]
    return dat

def x_slice(data):
    dat = np.empty(data.shape[1:], dtype=data.dtype)
    dat[:] = data[0,:,:]
    return dat

main()

首先让我们看一下HDF的性能:

jofer at cornbread in ~ 
$ sudo ./clear_cache.sh

jofer at cornbread in ~ 
$ time python chunked_hdf.py z
python chunked_hdf.py z  0.64s user 0.28s system 3% cpu 23.800 total

jofer at cornbread in ~ 
$ sudo ./clear_cache.sh

jofer at cornbread in ~ 
$ time python chunked_hdf.py x
python chunked_hdf.py x  0.12s user 0.30s system 1% cpu 21.856 total

“完整的” x切片和“完整的” z切片大约需要相同的时间(约20秒)。考虑到这是一个8GB的阵列,还算不错。大多数时候

并且,如果将其与映射的数组时间进行比较(按Fortran顺序排序:最佳情况是“ z切片”,最坏情况是“ x切片”):

jofer at cornbread in ~ 
$ sudo ./clear_cache.sh

jofer at cornbread in ~ 
$ time python memmapped_array.py z
python memmapped_array.py z  0.07s user 0.04s system 28% cpu 0.385 total

jofer at cornbread in ~ 
$ sudo ./clear_cache.sh

jofer at cornbread in ~ 
$ time python memmapped_array.py x
python memmapped_array.py x  2.46s user 37.24s system 0% cpu 3:35:26.85 total

是的,你看的没错。一个切片方向为0.3秒,另一个切片方向为〜3.5 小时

在“ x”方向上进行切片的时间远远大于将整个8GB阵列加载到内存中并选择所需切片所需的时间!(同样,这是一个按Fortran顺序排列的数组。对于C顺序的数组来说,相反的x / z slice时序将是这种情况。)

但是,如果我们一直想沿最佳情况方向进行切片,那么磁盘上的大二进制数组就非常好。(〜0.3秒!)

对于映射阵列,您将陷入这种I / O差异(或者各向异性是一个更好的说法)。但是,对于分块的HDF数据集,您可以选择分块大小,以使访问权限相等或针对特定用例进行了优化。它为您提供了更多的灵活性。

综上所述

希望这可以帮助您至少部分解决问题。HDF5与“原始”内存映射相比还具有许多其他优点,但是我在这里没有足够的空间来扩展它们。压缩可以加快某些速度(我处理的数据不能从压缩中获得很多好处,因此我很少使用它),并且与“原始”内存映射相比,HDF5文件对OS级缓存的播放效果更好。除此之外,HDF5是一种非常出色的容器格式。它为您提供了很大的灵活性来管理数据,并且可以或多或少地使用任何编程语言来使用它。

总体而言,尝试一下,看看它是否适合您的用例。我想您可能会感到惊讶。

HDF5 Advantages: Organization, flexibility, interoperability

Some of the main advantages of HDF5 are its hierarchical structure (similar to folders/files), optional arbitrary metadata stored with each item, and its flexibility (e.g. compression). This organizational structure and metadata storage may sound trivial, but it’s very useful in practice.

Another advantage of HDF is that the datasets can be either fixed-size or flexibly sized. Therefore, it’s easy to append data to a large dataset without having to create an entire new copy.

Additionally, HDF5 is a standardized format with libraries available for almost any language, so sharing your on-disk data between, say Matlab, Fortran, R, C, and Python is very easy with HDF. (To be fair, it’s not too hard with a big binary array, too, as long as you’re aware of the C vs. F ordering and know the shape, dtype, etc of the stored array.)

HDF advantages for a large array: Faster I/O of an arbitrary slice

Just as the TL/DR: For an ~8GB 3D array, reading a “full” slice along any axis took ~20 seconds with a chunked HDF5 dataset, and 0.3 seconds (best-case) to over three hours (worst case) for a memmapped array of the same data.

Beyond the things listed above, there’s another big advantage to a “chunked”* on-disk data format such as HDF5: Reading an arbitrary slice (emphasis on arbitrary) will typically be much faster, as the on-disk data is more contiguous on average.

*(HDF5 doesn’t have to be a chunked data format. It supports chunking, but doesn’t require it. In fact, the default for creating a dataset in h5py is not to chunk, if I recall correctly.)

Basically, your best case disk-read speed and your worst case disk read speed for a given slice of your dataset will be fairly close with a chunked HDF dataset (assuming you chose a reasonable chunk size or let a library choose one for you). With a simple binary array, the best-case is faster, but the worst-case is much worse.

One caveat, if you have an SSD, you likely won’t notice a huge difference in read/write speed. With a regular hard drive, though, sequential reads are much, much faster than random reads. (i.e. A regular hard drive has long seek time.) HDF still has an advantage on an SSD, but it’s more due its other features (e.g. metadata, organization, etc) than due to raw speed.


First off, to clear up confusion, accessing an h5py dataset returns an object that behaves fairly similarly to a numpy array, but does not load the data into memory until it’s sliced. (Similar to memmap, but not identical.) Have a look at the h5py introduction for more information.

Slicing the dataset will load a subset of the data into memory, but presumably you want to do something with it, at which point you’ll need it in memory anyway.

If you do want to do out-of-core computations, you can fairly easily for tabular data with pandas or pytables. It is possible with h5py (nicer for big N-D arrays), but you need to drop down to a touch lower level and handle the iteration yourself.

However, the future of numpy-like out-of-core computations is Blaze. Have a look at it if you really want to take that route.


The “unchunked” case

First off, consider a 3D C-ordered array written to disk (I’ll simulate it by calling arr.ravel() and printing the result, to make things more visible):

In [1]: import numpy as np

In [2]: arr = np.arange(4*6*6).reshape(4,6,6)

In [3]: arr
Out[3]:
array([[[  0,   1,   2,   3,   4,   5],
        [  6,   7,   8,   9,  10,  11],
        [ 12,  13,  14,  15,  16,  17],
        [ 18,  19,  20,  21,  22,  23],
        [ 24,  25,  26,  27,  28,  29],
        [ 30,  31,  32,  33,  34,  35]],

       [[ 36,  37,  38,  39,  40,  41],
        [ 42,  43,  44,  45,  46,  47],
        [ 48,  49,  50,  51,  52,  53],
        [ 54,  55,  56,  57,  58,  59],
        [ 60,  61,  62,  63,  64,  65],
        [ 66,  67,  68,  69,  70,  71]],

       [[ 72,  73,  74,  75,  76,  77],
        [ 78,  79,  80,  81,  82,  83],
        [ 84,  85,  86,  87,  88,  89],
        [ 90,  91,  92,  93,  94,  95],
        [ 96,  97,  98,  99, 100, 101],
        [102, 103, 104, 105, 106, 107]],

       [[108, 109, 110, 111, 112, 113],
        [114, 115, 116, 117, 118, 119],
        [120, 121, 122, 123, 124, 125],
        [126, 127, 128, 129, 130, 131],
        [132, 133, 134, 135, 136, 137],
        [138, 139, 140, 141, 142, 143]]])

The values would be stored on-disk sequentially as shown on line 4 below. (Let’s ignore filesystem details and fragmentation for the moment.)

In [4]: arr.ravel(order='C')
Out[4]:
array([  0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,
        13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,
        26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,
        39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,
        52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,  64,
        65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,
        78,  79,  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,
        91,  92,  93,  94,  95,  96,  97,  98,  99, 100, 101, 102, 103,
       104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
       117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
       130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143])

In the best case scenario, let’s take a slice along the first axis. Notice that these are just the first 36 values of the array. This will be a very fast read! (one seek, one read)

In [5]: arr[0,:,:]
Out[5]:
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23],
       [24, 25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34, 35]])

Similarly, the next slice along the first axis will just be the next 36 values. To read a complete slice along this axis, we only need one seek operation. If all we’re going to be reading is various slices along this axis, then this is the perfect file structure.

However, let’s consider the worst-case scenario: A slice along the last axis.

In [6]: arr[:,:,0]
Out[6]:
array([[  0,   6,  12,  18,  24,  30],
       [ 36,  42,  48,  54,  60,  66],
       [ 72,  78,  84,  90,  96, 102],
       [108, 114, 120, 126, 132, 138]])

To read this slice in, we need 36 seeks and 36 reads, as all of the values are separated on disk. None of them are adjacent!

This may seem pretty minor, but as we get to larger and larger arrays, the number and size of the seek operations grows rapidly. For a large-ish (~10Gb) 3D array stored in this way and read in via memmap, reading a full slice along the “worst” axis can easily take tens of minutes, even with modern hardware. At the same time, a slice along the best axis can take less than a second. For simplicity, I’m only showing “full” slices along a single axis, but the exact same thing happens with arbitrary slices of any subset of the data.

Incidentally there are several file formats that take advantage of this and basically store three copies of huge 3D arrays on disk: one in C-order, one in F-order, and one in the intermediate between the two. (An example of this is Geoprobe’s D3D format, though I’m not sure it’s documented anywhere.) Who cares if the final file size is 4TB, storage is cheap! The crazy thing about that is that because the main use case is extracting a single sub-slice in each direction, the reads you want to make are very, very fast. It works very well!


The simple “chunked” case

Let’s say we store 2x2x2 “chunks” of the 3D array as contiguous blocks on disk. In other words, something like:

nx, ny, nz = arr.shape
slices = []
for i in range(0, nx, 2):
    for j in range(0, ny, 2):
        for k in range(0, nz, 2):
            slices.append((slice(i, i+2), slice(j, j+2), slice(k, k+2)))

chunked = np.hstack([arr[chunk].ravel() for chunk in slices])

So the data on disk would look like chunked:

array([  0,   1,   6,   7,  36,  37,  42,  43,   2,   3,   8,   9,  38,
        39,  44,  45,   4,   5,  10,  11,  40,  41,  46,  47,  12,  13,
        18,  19,  48,  49,  54,  55,  14,  15,  20,  21,  50,  51,  56,
        57,  16,  17,  22,  23,  52,  53,  58,  59,  24,  25,  30,  31,
        60,  61,  66,  67,  26,  27,  32,  33,  62,  63,  68,  69,  28,
        29,  34,  35,  64,  65,  70,  71,  72,  73,  78,  79, 108, 109,
       114, 115,  74,  75,  80,  81, 110, 111, 116, 117,  76,  77,  82,
        83, 112, 113, 118, 119,  84,  85,  90,  91, 120, 121, 126, 127,
        86,  87,  92,  93, 122, 123, 128, 129,  88,  89,  94,  95, 124,
       125, 130, 131,  96,  97, 102, 103, 132, 133, 138, 139,  98,  99,
       104, 105, 134, 135, 140, 141, 100, 101, 106, 107, 136, 137, 142, 143])

And just to show that they’re 2x2x2 blocks of arr, notice that these are the first 8 values of chunked:

In [9]: arr[:2, :2, :2]
Out[9]:
array([[[ 0,  1],
        [ 6,  7]],

       [[36, 37],
        [42, 43]]])

To read in any slice along an axis, we’d read in either 6 or 9 contiguous chunks (twice as much data as we need) and then only keep the portion we wanted. That’s a worst-case maximum of 9 seeks vs a maximum of 36 seeks for the non-chunked version. (But the best case is still 6 seeks vs 1 for the memmapped array.) Because sequential reads are very fast compared to seeks, this significantly reduces the amount of time it takes to read an arbitrary subset into memory. Once again, this effect becomes larger with larger arrays.

HDF5 takes this a few steps farther. The chunks don’t have to be stored contiguously, and they’re indexed by a B-Tree. Furthermore, they don’t have to be the same size on disk, so compression can be applied to each chunk.


Chunked arrays with h5py

By default, h5py doesn’t created chunked HDF files on disk (I think pytables does, by contrast). If you specify chunks=True when creating the dataset, however, you’ll get a chunked array on disk.

As a quick, minimal example:

import numpy as np
import h5py

data = np.random.random((100, 100, 100))

with h5py.File('test.hdf', 'w') as outfile:
    dset = outfile.create_dataset('a_descriptive_name', data=data, chunks=True)
    dset.attrs['some key'] = 'Did you want some metadata?'

Note that chunks=True tells h5py to automatically pick a chunk size for us. If you know more about your most common use-case, you can optimize the chunk size/shape by specifying a shape tuple (e.g. (2,2,2) in the simple example above). This allows you to make reads along a particular axis more efficient or optimize for reads/writes of a certain size.


I/O Performance comparison

Just to emphasize the point, let’s compare reading in slices from a chunked HDF5 dataset and a large (~8GB), Fortran-ordered 3D array containing the same exact data.

I’ve cleared all OS caches between each run, so we’re seeing the “cold” performance.

For each file type, we’ll test reading in a “full” x-slice along the first axis and a “full” z-slize along the last axis. For the Fortran-ordered memmapped array, the “x” slice is the worst case, and the “z” slice is the best case.

The code used is in a gist (including creating the hdf file). I can’t easily share the data used here, but you could simulate it by an array of zeros of the same shape (621, 4991, 2600) and type np.uint8.

The chunked_hdf.py looks like this:

import sys
import h5py

def main():
    data = read()

    if sys.argv[1] == 'x':
        x_slice(data)
    elif sys.argv[1] == 'z':
        z_slice(data)

def read():
    f = h5py.File('/tmp/test.hdf5', 'r')
    return f['seismic_volume']

def z_slice(data):
    return data[:,:,0]

def x_slice(data):
    return data[0,:,:]

main()

memmapped_array.py is similar, but has a touch more complexity to ensure the slices are actually loaded into memory (by default, another memmapped array would be returned, which wouldn’t be an apples-to-apples comparison).

import numpy as np
import sys

def main():
    data = read()

    if sys.argv[1] == 'x':
        x_slice(data)
    elif sys.argv[1] == 'z':
        z_slice(data)

def read():
    big_binary_filename = '/data/nankai/data/Volumes/kumdep01_flipY.3dv.vol'
    shape = 621, 4991, 2600
    header_len = 3072

    data = np.memmap(filename=big_binary_filename, mode='r', offset=header_len,
                     order='F', shape=shape, dtype=np.uint8)
    return data

def z_slice(data):
    dat = np.empty(data.shape[:2], dtype=data.dtype)
    dat[:] = data[:,:,0]
    return dat

def x_slice(data):
    dat = np.empty(data.shape[1:], dtype=data.dtype)
    dat[:] = data[0,:,:]
    return dat

main()

Let’s have a look at the HDF performance first:

jofer at cornbread in ~ 
$ sudo ./clear_cache.sh

jofer at cornbread in ~ 
$ time python chunked_hdf.py z
python chunked_hdf.py z  0.64s user 0.28s system 3% cpu 23.800 total

jofer at cornbread in ~ 
$ sudo ./clear_cache.sh

jofer at cornbread in ~ 
$ time python chunked_hdf.py x
python chunked_hdf.py x  0.12s user 0.30s system 1% cpu 21.856 total

A “full” x-slice and a “full” z-slice take about the same amount of time (~20sec). Considering this is an 8GB array, that’s not too bad. Most of the time

And if we compare this to the memmapped array times (it’s Fortran-ordered: A “z-slice” is the best case and an “x-slice” is the worst case.):

jofer at cornbread in ~ 
$ sudo ./clear_cache.sh

jofer at cornbread in ~ 
$ time python memmapped_array.py z
python memmapped_array.py z  0.07s user 0.04s system 28% cpu 0.385 total

jofer at cornbread in ~ 
$ sudo ./clear_cache.sh

jofer at cornbread in ~ 
$ time python memmapped_array.py x
python memmapped_array.py x  2.46s user 37.24s system 0% cpu 3:35:26.85 total

Yes, you read that right. 0.3 seconds for one slice direction and ~3.5 hours for the other.

The time to slice in the “x” direction is far longer than the amount of time it would take to load the entire 8GB array into memory and select the slice we wanted! (Again, this is a Fortran-ordered array. The opposite x/z slice timing would be the case for a C-ordered array.)

However, if we’re always wanting to take a slice along the best-case direction, the big binary array on disk is very good. (~0.3 sec!)

With a memmapped array, you’re stuck with this I/O discrepancy (or perhaps anisotropy is a better term). However, with a chunked HDF dataset, you can choose the chunksize such that access is either equal or is optimized for a particular use-case. It gives you a lot more flexibility.

In summary

Hopefully that helps clear up one part of your question, at any rate. HDF5 has many other advantages over “raw” memmaps, but I don’t have room to expand on all of them here. Compression can speed some things up (the data I work with doesn’t benefit much from compression, so I rarely use it), and OS-level caching often plays more nicely with HDF5 files than with “raw” memmaps. Beyond that, HDF5 is a really fantastic container format. It gives you a lot of flexibility in managing your data, and can be used from more or less any programming language.

Overall, try it and see if it works well for your use case. I think you might be surprised.


从NumPy数组中选择特定的行和列

问题:从NumPy数组中选择特定的行和列

我一直在发疯,试图找出我在这里做错了什么愚蠢的事情。

我正在使用NumPy,并且我想从中选择特定的行索引和特定的列索引。这是我的问题的要点:

import numpy as np

a = np.arange(20).reshape((5,4))
# array([[ 0,  1,  2,  3],
#        [ 4,  5,  6,  7],
#        [ 8,  9, 10, 11],
#        [12, 13, 14, 15],
#        [16, 17, 18, 19]])

# If I select certain rows, it works
print a[[0, 1, 3], :]
# array([[ 0,  1,  2,  3],
#        [ 4,  5,  6,  7],
#        [12, 13, 14, 15]])

# If I select certain rows and a single column, it works
print a[[0, 1, 3], 2]
# array([ 2,  6, 14])

# But if I select certain rows AND certain columns, it fails
print a[[0,1,3], [0,2]]
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# ValueError: shape mismatch: objects cannot be broadcast to a single shape

为什么会这样呢?我当然应该能够选择第一行,第二行和第四行以及第一列和第三列?我期望的结果是:

a[[0,1,3], [0,2]] => [[0,  2],
                      [4,  6],
                      [12, 14]]

I’ve been going crazy trying to figure out what stupid thing I’m doing wrong here.

I’m using NumPy, and I have specific row indices and specific column indices that I want to select from. Here’s the gist of my problem:

import numpy as np

a = np.arange(20).reshape((5,4))
# array([[ 0,  1,  2,  3],
#        [ 4,  5,  6,  7],
#        [ 8,  9, 10, 11],
#        [12, 13, 14, 15],
#        [16, 17, 18, 19]])

# If I select certain rows, it works
print a[[0, 1, 3], :]
# array([[ 0,  1,  2,  3],
#        [ 4,  5,  6,  7],
#        [12, 13, 14, 15]])

# If I select certain rows and a single column, it works
print a[[0, 1, 3], 2]
# array([ 2,  6, 14])

# But if I select certain rows AND certain columns, it fails
print a[[0,1,3], [0,2]]
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# ValueError: shape mismatch: objects cannot be broadcast to a single shape

Why is this happening? Surely I should be able to select the 1st, 2nd, and 4th rows, and 1st and 3rd columns? The result I’m expecting is:

a[[0,1,3], [0,2]] => [[0,  2],
                      [4,  6],
                      [12, 14]]

回答 0

花式索引要求您提供每个维度的所有索引。您为第一个提供3个索引,为第二个仅提供2个索引,因此会出现错误。您想做这样的事情:

>>> a[[[0, 0], [1, 1], [3, 3]], [[0,2], [0,2], [0, 2]]]
array([[ 0,  2],
       [ 4,  6],
       [12, 14]])

当然写这很痛苦,所以您可以让广播帮助您:

>>> a[[[0], [1], [3]], [0, 2]]
array([[ 0,  2],
       [ 4,  6],
       [12, 14]])

如果您使用数组而不是列表建立索引,则此操作要简单得多:

>>> row_idx = np.array([0, 1, 3])
>>> col_idx = np.array([0, 2])
>>> a[row_idx[:, None], col_idx]
array([[ 0,  2],
       [ 4,  6],
       [12, 14]])

Fancy indexing requires you to provide all indices for each dimension. You are providing 3 indices for the first one, and only 2 for the second one, hence the error. You want to do something like this:

>>> a[[[0, 0], [1, 1], [3, 3]], [[0,2], [0,2], [0, 2]]]
array([[ 0,  2],
       [ 4,  6],
       [12, 14]])

That is of course a pain to write, so you can let broadcasting help you:

>>> a[[[0], [1], [3]], [0, 2]]
array([[ 0,  2],
       [ 4,  6],
       [12, 14]])

This is much simpler to do if you index with arrays, not lists:

>>> row_idx = np.array([0, 1, 3])
>>> col_idx = np.array([0, 2])
>>> a[row_idx[:, None], col_idx]
array([[ 0,  2],
       [ 4,  6],
       [12, 14]])

回答 1

至于全胜表明,一个简单的黑客是只选择第一行,然后选择在列

>>> a[[0,1,3], :]            # Returns the rows you want
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [12, 13, 14, 15]])
>>> a[[0,1,3], :][:, [0,2]]  # Selects the columns you want as well
array([[ 0,  2],
       [ 4,  6],
       [12, 14]])

[编辑]内置方法: np.ix_

最近,我发现numpy为您提供了内置的一线功能,可以准确执行@Jaime的建议,而不必使用广播语法(由于缺乏可读性)。从文档:

使用ix_可以快速构建索引数组,该索引数组将对叉积进行索引。a[np.ix_([1,3],[2,5])]返回数组[[a[1,2] a[1,5]], [a[3,2] a[3,5]]]

因此,您可以这样使用它:

>>> a = np.arange(20).reshape((5,4))
>>> a[np.ix_([0,1,3], [0,2])]
array([[ 0,  2],
       [ 4,  6],
       [12, 14]])

而且它的工作方式是像Jaime所建议的那样照顾数组的对齐,以便正确进行广播:

>>> np.ix_([0,1,3], [0,2])
(array([[0],
        [1],
        [3]]), array([[0, 2]]))

而且,正如MikeC在评论中说的那样,它np.ix_具有返回视图的优点,而我的第一个(预编辑)答案没有。这意味着您现在可以分配给索引数组:

>>> a[np.ix_([0,1,3], [0,2])] = -1
>>> a    
array([[-1,  1, -1,  3],
       [-1,  5, -1,  7],
       [ 8,  9, 10, 11],
       [-1, 13, -1, 15],
       [16, 17, 18, 19]])

As Toan suggests, a simple hack would be to just select the rows first, and then select the columns over that.

>>> a[[0,1,3], :]            # Returns the rows you want
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [12, 13, 14, 15]])
>>> a[[0,1,3], :][:, [0,2]]  # Selects the columns you want as well
array([[ 0,  2],
       [ 4,  6],
       [12, 14]])

[Edit] The built-in method: np.ix_

I recently discovered that numpy gives you an in-built one-liner to doing exactly what @Jaime suggested, but without having to use broadcasting syntax (which suffers from lack of readability). From the docs:

Using ix_ one can quickly construct index arrays that will index the cross product. a[np.ix_([1,3],[2,5])] returns the array [[a[1,2] a[1,5]], [a[3,2] a[3,5]]].

So you use it like this:

>>> a = np.arange(20).reshape((5,4))
>>> a[np.ix_([0,1,3], [0,2])]
array([[ 0,  2],
       [ 4,  6],
       [12, 14]])

And the way it works is that it takes care of aligning arrays the way Jaime suggested, so that broadcasting happens properly:

>>> np.ix_([0,1,3], [0,2])
(array([[0],
        [1],
        [3]]), array([[0, 2]]))

Also, as MikeC says in a comment, np.ix_ has the advantage of returning a view, which my first (pre-edit) answer did not. This means you can now assign to the indexed array:

>>> a[np.ix_([0,1,3], [0,2])] = -1
>>> a    
array([[-1,  1, -1,  3],
       [-1,  5, -1,  7],
       [ 8,  9, 10, 11],
       [-1, 13, -1, 15],
       [16, 17, 18, 19]])

回答 2

使用:

 >>> a[[0,1,3]][:,[0,2]]
array([[ 0,  2],
   [ 4,  6],
   [12, 14]])

要么:

>>> a[[0,1,3],::2]
array([[ 0,  2],
   [ 4,  6],
   [12, 14]])

USE:

 >>> a[[0,1,3]][:,[0,2]]
array([[ 0,  2],
   [ 4,  6],
   [12, 14]])

OR:

>>> a[[0,1,3],::2]
array([[ 0,  2],
   [ 4,  6],
   [12, 14]])

回答 3

使用np.ix_是最方便的方法(有人回答),但这是另一种有趣的方法:

>>> rows = [0, 1, 3]
>>> cols = [0, 2]

>>> a[rows].T[cols].T

array([[ 0,  2],
       [ 4,  6],
       [12, 14]])

Using np.ix_ is the most convenient way to do it (as answered by others), but here is another interesting way to do it:

>>> rows = [0, 1, 3]
>>> cols = [0, 2]

>>> a[rows].T[cols].T

array([[ 0,  2],
       [ 4,  6],
       [12, 14]])

在TensorFlow中使用预训练的单词嵌入(word2vec或Glove)

问题:在TensorFlow中使用预训练的单词嵌入(word2vec或Glove)

我最近审查了卷积文本分类的一个有趣的实现。但是我检查过的所有TensorFlow代码都使用如下的随机(未经预训练)嵌入向量:

with tf.device('/cpu:0'), tf.name_scope("embedding"):
    W = tf.Variable(
        tf.random_uniform([vocab_size, embedding_size], -1.0, 1.0),
        name="W")
    self.embedded_chars = tf.nn.embedding_lookup(W, self.input_x)
    self.embedded_chars_expanded = tf.expand_dims(self.embedded_chars, -1)

有谁知道如何使用Word2vec或GloVe预训练词嵌入的结果,而不是随机的结果?

I’ve recently reviewed an interesting implementation for convolutional text classification. However all TensorFlow code I’ve reviewed uses a random (not pre-trained) embedding vectors like the following:

with tf.device('/cpu:0'), tf.name_scope("embedding"):
    W = tf.Variable(
        tf.random_uniform([vocab_size, embedding_size], -1.0, 1.0),
        name="W")
    self.embedded_chars = tf.nn.embedding_lookup(W, self.input_x)
    self.embedded_chars_expanded = tf.expand_dims(self.embedded_chars, -1)

Does anybody know how to use the results of Word2vec or a GloVe pre-trained word embedding instead of a random one?


回答 0

您可以通过多种方式在TensorFlow中使用预训练的嵌入。假设你有一个与NumPy阵列称为嵌入embedding,用vocab_size行和embedding_dim列,要创建一个张量W,可以在一个呼叫中使用tf.nn.embedding_lookup()

  1. 只需创建W一个tf.constant()是需要embedding为它的价值:

    W = tf.constant(embedding, name="W")

    这是最简单的方法,但是由于a的值tf.constant()多次存储在内存中,因此内存使用效率不高。由于embedding可能很大,因此您仅应将此方法用于玩具示例。

  2. 创建W为a tf.Variable并通过NumPy数组对其进行初始化tf.placeholder()

    W = tf.Variable(tf.constant(0.0, shape=[vocab_size, embedding_dim]),
                    trainable=False, name="W")
    
    embedding_placeholder = tf.placeholder(tf.float32, [vocab_size, embedding_dim])
    embedding_init = W.assign(embedding_placeholder)
    
    # ...
    sess = tf.Session()
    
    sess.run(embedding_init, feed_dict={embedding_placeholder: embedding})
    

    这样可以避免embedding在图表中存储的副本,但确实需要足够的内存才能一次在内存中保留矩阵的两个副本(一个用于NumPy数组,一个用于tf.Variable)。请注意,我假设您想在训练期间保持嵌入矩阵不变,因此W是使用创建的trainable=False

  3. 如果将嵌入训练为另一个TensorFlow模型的一部分,则可以使用tf.train.Saver从其他模型的检查点文件加载值。这意味着嵌入矩阵可以完全绕过Python。W按照选项2 创建,然后执行以下操作:

    W = tf.Variable(...)
    
    embedding_saver = tf.train.Saver({"name_of_variable_in_other_model": W})
    
    # ...
    sess = tf.Session()
    embedding_saver.restore(sess, "checkpoint_filename.ckpt")
    

There are a few ways that you can use a pre-trained embedding in TensorFlow. Let’s say that you have the embedding in a NumPy array called embedding, with vocab_size rows and embedding_dim columns and you want to create a tensor W that can be used in a call to tf.nn.embedding_lookup().

  1. Simply create W as a tf.constant() that takes embedding as its value:

    W = tf.constant(embedding, name="W")
    

    This is the easiest approach, but it is not memory efficient because the value of a tf.constant() is stored multiple times in memory. Since embedding can be very large, you should only use this approach for toy examples.

  2. Create W as a tf.Variable and initialize it from the NumPy array via a tf.placeholder():

    W = tf.Variable(tf.constant(0.0, shape=[vocab_size, embedding_dim]),
                    trainable=False, name="W")
    
    embedding_placeholder = tf.placeholder(tf.float32, [vocab_size, embedding_dim])
    embedding_init = W.assign(embedding_placeholder)
    
    # ...
    sess = tf.Session()
    
    sess.run(embedding_init, feed_dict={embedding_placeholder: embedding})
    

    This avoid storing a copy of embedding in the graph, but it does require enough memory to keep two copies of the matrix in memory at once (one for the NumPy array, and one for the tf.Variable). Note that I’ve assumed that you want to hold the embedding matrix constant during training, so W is created with trainable=False.

  3. If the embedding was trained as part of another TensorFlow model, you can use a tf.train.Saver to load the value from the other model’s checkpoint file. This means that the embedding matrix can bypass Python altogether. Create W as in option 2, then do the following:

    W = tf.Variable(...)
    
    embedding_saver = tf.train.Saver({"name_of_variable_in_other_model": W})
    
    # ...
    sess = tf.Session()
    embedding_saver.restore(sess, "checkpoint_filename.ckpt")
    

回答 1

我使用这种方法来加载和共享嵌入。

W = tf.get_variable(name="W", shape=embedding.shape, initializer=tf.constant_initializer(embedding), trainable=False)

I use this method to load and share embedding.

W = tf.get_variable(name="W", shape=embedding.shape, initializer=tf.constant_initializer(embedding), trainable=False)

回答 2

@mrry的答案不正确,因为它会导致覆盖每个运行网络的嵌入权重,因此,如果您采用小批量方法来训练网络,则将覆盖嵌入权重。因此,以我的观点,预训练嵌入的正确方法是:

embeddings = tf.get_variable("embeddings", shape=[dim1, dim2], initializer=tf.constant_initializer(np.array(embeddings_matrix))

The answer of @mrry is not right because it provoques the overwriting of the embeddings weights each the network is run, so if you are following a minibatch approach to train your network, you are overwriting the weights of the embeddings. So, on my point of view the right way to pre-trained embeddings is:

embeddings = tf.get_variable("embeddings", shape=[dim1, dim2], initializer=tf.constant_initializer(np.array(embeddings_matrix))

回答 3

2.0兼容答案:有很多预训练的嵌入,这些嵌入是由Google开发的,并且是开源的。

其中一些是Universal Sentence Encoder (USE), ELMO, BERT,等等。在代码中重用它们非常容易。

重用代码Pre-Trained EmbeddingUniversal Sentence Encoder如下所示:

  !pip install "tensorflow_hub>=0.6.0"
  !pip install "tensorflow>=2.0.0"

  import tensorflow as tf
  import tensorflow_hub as hub

  module_url = "https://tfhub.dev/google/universal-sentence-encoder/4"
  embed = hub.KerasLayer(module_url)
  embeddings = embed(["A long sentence.", "single-word",
                      "http://example.com"])
  print(embeddings.shape)  #(3,128)

有关更多信息,请参见TF Hub Link,它是Google开发和开放源代码的预培训嵌入。

2.0 Compatible Answer: There are many Pre-Trained Embeddings, which are developed by Google and which have been Open Sourced.

Some of them are Universal Sentence Encoder (USE), ELMO, BERT, etc.. and it is very easy to reuse them in your code.

Code to reuse the Pre-Trained Embedding, Universal Sentence Encoder is shown below:

  !pip install "tensorflow_hub>=0.6.0"
  !pip install "tensorflow>=2.0.0"

  import tensorflow as tf
  import tensorflow_hub as hub

  module_url = "https://tfhub.dev/google/universal-sentence-encoder/4"
  embed = hub.KerasLayer(module_url)
  embeddings = embed(["A long sentence.", "single-word",
                      "http://example.com"])
  print(embeddings.shape)  #(3,128)

For more information the Pre-Trained Embeddings developed and open-sourced by Google, refer TF Hub Link.


回答 4

在Tensorflow版本2中,如果您使用Embedding层,则非常简单

X=tf.keras.layers.Embedding(input_dim=vocab_size,
                            output_dim=300,
                            input_length=Length_of_input_sequences,
                            embeddings_initializer=matrix_of_pretrained_weights
                            )(ur_inp)

With tensorflow version 2 its quite easy if you use the Embedding layer

X=tf.keras.layers.Embedding(input_dim=vocab_size,
                            output_dim=300,
                            input_length=Length_of_input_sequences,
                            embeddings_initializer=matrix_of_pretrained_weights
                            )(ur_inp)


回答 5

我也遇到嵌入问题,所以我写了有关数据集的详细教程。在这里我想补充一下我尝试过的方法也可以尝试这种方法,

import tensorflow as tf

tf.reset_default_graph()

input_x=tf.placeholder(tf.int32,shape=[None,None])

#you have to edit shape according to your embedding size


Word_embedding = tf.get_variable(name="W", shape=[400000,100], initializer=tf.constant_initializer(np.array(word_embedding)), trainable=False)
embedding_loopup= tf.nn.embedding_lookup(Word_embedding,input_x)

with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        for ii in final_:
            print(sess.run(embedding_loopup,feed_dict={input_x:[ii]}))

如果您想从头开始理解,请看这里工作详细的Tutorial Ipython示例

I was also facing embedding issue, So i wrote detailed tutorial with dataset. Here I would like to add what I tried You can also try this method,

import tensorflow as tf

tf.reset_default_graph()

input_x=tf.placeholder(tf.int32,shape=[None,None])

#you have to edit shape according to your embedding size


Word_embedding = tf.get_variable(name="W", shape=[400000,100], initializer=tf.constant_initializer(np.array(word_embedding)), trainable=False)
embedding_loopup= tf.nn.embedding_lookup(Word_embedding,input_x)

with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        for ii in final_:
            print(sess.run(embedding_loopup,feed_dict={input_x:[ii]}))

Here is working detailed Tutorial Ipython example if you want to understand from scratch , take a look .


大小调整/缩放图像

问题:大小调整/缩放图像

我想拍摄一张图像并更改图像的比例,虽然它是一个numpy数组。

例如,我有一个可口可乐瓶的图像: bottle-1

转换为一个numpy的形状数组,(528, 203, 3)我想调整其大小以表示第二个图像的大小: bottle-2

形状为(140, 54, 3)

如何在保持原始图像的同时将图像尺寸更改为特定形状?其他答案建议将每两行或第三行剥离掉,但是我想要做的基本上是像通过图像编辑器那样缩小图像,但是使用python代码。是否有任何库可以在numpy / SciPy中执行此操作?

I would like to take an image and change the scale of the image, while it is a numpy array.

For example I have this image of a coca-cola bottle: bottle-1

Which translates to a numpy array of shape (528, 203, 3) and I want to resize that to say the size of this second image: bottle-2

Which has a shape of (140, 54, 3).

How do I change the size of the image to a certain shape while still maintaining the original image? Other answers suggest stripping every other or third row out, but what I want to do is basically shrink the image how you would via an image editor but in python code. Are there any libraries to do this in numpy/SciPy?


回答 0

是的,您可以安装opencv(这是用于图像处理和计算机视觉的库),然后使用该cv2.resize功能。例如使用:

import cv2
import numpy as np

img = cv2.imread('your_image.jpg')
res = cv2.resize(img, dsize=(54, 140), interpolation=cv2.INTER_CUBIC)

img因此,这是一个包含原始图像res的numpy数组,而这是一个包含调整大小的图像的numpy数组。interpolation参数的一个重要方面是:有几种方法可以调整图像的大小。特别是因为你缩小图像,而原图像的大小是不是调整后的图像的大小的倍数。可能的插值方案为:

  • INTER_NEAREST -最近邻插值
  • INTER_LINEAR -双线性插值(默认使用)
  • INTER_AREA-使用像素面积关系进行重采样。这可能是首选的图像抽取方法,因为它可提供无波纹的结果。但是,当图像放大时,它与INTER_NEAREST方法类似 。
  • INTER_CUBIC -在4×4像素邻域上的双三次插值
  • INTER_LANCZOS4 -在8×8像素邻域上进行Lanczos插值

与大多数选项一样,就每种调整大小模式而言,也没有“最佳”选项,在某些情况下,一种策略可能比另一种策略更可取。

Yeah, you can install opencv (this is a library used for image processing, and computer vision), and use the cv2.resize function. And for instance use:

import cv2
import numpy as np

img = cv2.imread('your_image.jpg')
res = cv2.resize(img, dsize=(54, 140), interpolation=cv2.INTER_CUBIC)

Here img is thus a numpy array containing the original image, whereas res is a numpy array containing the resized image. An important aspect is the interpolation parameter: there are several ways how to resize an image. Especially since you scale down the image, and the size of the original image is not a multiple of the size of the resized image. Possible interpolation schemas are:

  • INTER_NEAREST – a nearest-neighbor interpolation
  • INTER_LINEAR – a bilinear interpolation (used by default)
  • INTER_AREA – resampling using pixel area relation. It may be a preferred method for image decimation, as it gives moire’-free results. But when the image is zoomed, it is similar to the INTER_NEAREST method.
  • INTER_CUBIC – a bicubic interpolation over 4×4 pixel neighborhood
  • INTER_LANCZOS4 – a Lanczos interpolation over 8×8 pixel neighborhood

Like with most options, there is no “best” option in the sense that for every resize schema, there are scenarios where one strategy can be preferred over another.


回答 1

尽管可以单独使用numpy来执行此操作,但该操作不是内置的。也就是说,您可以使用scikit-image(基于numpy构建)执行这种图像处理。

Scikit-Image重缩放文档在此处

例如,您可以对图像执行以下操作:

from skimage.transform import resize
bottle_resized = resize(bottle, (140, 54))

这将为您处理插值,抗锯齿等问题。

While it might be possible to use numpy alone to do this, the operation is not built-in. That said, you can use scikit-image (which is built on numpy) to do this kind of image manipulation.

Scikit-Image rescaling documentation is here.

For example, you could do the following with your image:

from skimage.transform import resize
bottle_resized = resize(bottle, (140, 54))

This will take care of things like interpolation, anti-aliasing, etc. for you.


回答 2

对于来自Google的人们来说,他们正在寻找一种快速降序对numpy数组图像进行下采样以供机器学习应用程序使用的方法,这是一种超快速方法(从此处改编)。仅当输入尺寸为输出尺寸的倍数时,此方法才有效。

以下示例将采样率从128×128降采样为64×64(可以轻松更改)。

频道最后订购

# large image is shape (128, 128, 3)
# small image is shape (64, 64, 3)
input_size = 128
output_size = 64
bin_size = input_size // output_size
small_image = large_image.reshape((output_size, bin_size, 
                                   output_size, bin_size, 3)).max(3).max(1)

渠道第一订购

# large image is shape (3, 128, 128)
# small image is shape (3, 64, 64)
input_size = 128
output_size = 64
bin_size = input_size // output_size
small_image = large_image.reshape((3, output_size, bin_size, 
                                      output_size, bin_size)).max(4).max(2)

对于灰度图像,只需将更3改为1如下所示:

渠道第一订购

# large image is shape (1, 128, 128)
# small image is shape (1, 64, 64)
input_size = 128
output_size = 64
bin_size = input_size // output_size
small_image = large_image.reshape((1, output_size, bin_size,
                                      output_size, bin_size)).max(4).max(2)

此方法使用的是最大池化。我发现这是最快的方法。

For people coming here from Google looking for a fast way to downsample images in numpy arrays for use in Machine Learning applications, here’s a super fast method (adapted from here ). This method only works when the input dimensions are a multiple of the output dimensions.

The following examples downsample from 128×128 to 64×64 (this can be easily changed).

Channels last ordering

# large image is shape (128, 128, 3)
# small image is shape (64, 64, 3)
input_size = 128
output_size = 64
bin_size = input_size // output_size
small_image = large_image.reshape((output_size, bin_size, 
                                   output_size, bin_size, 3)).max(3).max(1)

Channels first ordering

# large image is shape (3, 128, 128)
# small image is shape (3, 64, 64)
input_size = 128
output_size = 64
bin_size = input_size // output_size
small_image = large_image.reshape((3, output_size, bin_size, 
                                      output_size, bin_size)).max(4).max(2)

For grayscale images just change the 3 to a 1 like this:

Channels first ordering

# large image is shape (1, 128, 128)
# small image is shape (1, 64, 64)
input_size = 128
output_size = 64
bin_size = input_size // output_size
small_image = large_image.reshape((1, output_size, bin_size,
                                      output_size, bin_size)).max(4).max(2)

This method uses the equivalent of max pooling. It’s the fastest way to do this that I’ve found.


回答 3

如果有人来这里寻找一种简单的方法来在Python中缩放/调整图像大小,而又不使用其他库,这是一个非常简单的图像调整大小功能:

#simple image scaling to (nR x nC) size
def scale(im, nR, nC):
  nR0 = len(im)     # source number of rows 
  nC0 = len(im[0])  # source number of columns 
  return [[ im[int(nR0 * r / nR)][int(nC0 * c / nC)]  
             for c in range(nC)] for r in range(nR)]

用法示例:将(30 x 30)图像调整为(100 x 200):

import matplotlib.pyplot as plt

def sqr(x):
  return x*x

def f(r, c, nR, nC):
  return 1.0 if sqr(c - nC/2) + sqr(r - nR/2) < sqr(nC/4) else 0.0

# a red circle on a canvas of size (nR x nC)
def circ(nR, nC):
  return [[ [f(r, c, nR, nC), 0, 0] 
             for c in range(nC)] for r in range(nR)]

plt.imshow(scale(circ(30, 30), 100, 200))

输出: 缩放图像

这可以缩小/缩放图像,并且可以与numpy数组一起使用。

If anyone came here looking for a simple method to scale/resize an image in Python, without using additional libraries, here’s a very simple image resize function:

#simple image scaling to (nR x nC) size
def scale(im, nR, nC):
  nR0 = len(im)     # source number of rows 
  nC0 = len(im[0])  # source number of columns 
  return [[ im[int(nR0 * r / nR)][int(nC0 * c / nC)]  
             for c in range(nC)] for r in range(nR)]

Example usage: resizing a (30 x 30) image to (100 x 200):

import matplotlib.pyplot as plt

def sqr(x):
  return x*x

def f(r, c, nR, nC):
  return 1.0 if sqr(c - nC/2) + sqr(r - nR/2) < sqr(nC/4) else 0.0

# a red circle on a canvas of size (nR x nC)
def circ(nR, nC):
  return [[ [f(r, c, nR, nC), 0, 0] 
             for c in range(nC)] for r in range(nR)]

plt.imshow(scale(circ(30, 30), 100, 200))

Output: scaled image

This works to shrink/scale images, and works fine with numpy arrays.


回答 4

SciPy的imresize()方法是另一种调整大小的方法,但是将从SciPy v 1.3.0开始将其删除。SciPy指的是PIL图像调整大小方法:Image.resize(size, resample=0)

size –请求的大小(以像素为单位),为2元组:(宽度,高度)。
重采样 –可选的重采样过滤器。这可以是PIL.Image.NEAREST(使用最近的邻居),PIL.Image.BILINEAR(线性插值),PIL.Image.BICUBIC(三次样条插值)或PIL.Image.LANCZOS(高质量的下采样滤波器)之一)。如果省略,或者图像的模式为“ 1”或“ P”,则将其设置为PIL.Image.NEAREST。

链接到这里:https : //pillow.readthedocs.io/en/3.1.x/reference/Image.html#PIL.Image.Image.resize

SciPy’s imresize() method was another resize method, but it will be removed starting with SciPy v 1.3.0 . SciPy refers to PIL image resize method: Image.resize(size, resample=0)

size – The requested size in pixels, as a 2-tuple: (width, height).
resample – An optional resampling filter. This can be one of PIL.Image.NEAREST (use nearest neighbour), PIL.Image.BILINEAR (linear interpolation), PIL.Image.BICUBIC (cubic spline interpolation), or PIL.Image.LANCZOS (a high-quality downsampling filter). If omitted, or if the image has mode “1” or “P”, it is set PIL.Image.NEAREST.

Link here: https://pillow.readthedocs.io/en/3.1.x/reference/Image.html#PIL.Image.Image.resize


回答 5

是否有任何库可以在numpy / SciPy中执行此操作

当然。您可以在没有OpenCV,scikit-image或PIL的情况下执行此操作。

图像调整大小基本上是将每个像素的坐标从原始图像映射到其调整大小的位置。

由于图像的坐标必须是整数(将其视为矩阵),因此,如果映射的坐标具有十进制值,则应插值像素值以使其接近整数位置(例如,已知最接近该位置的像素)作为最近邻插值)。

您所需要做的就是为您执行此插值的功能。SciPy有interpolate.interp2d

您可以使用它来调整numpy数组中图像的大小,例如arr,如下所示:

W, H = arr.shape[:2]
new_W, new_H = (600,300)
xrange = lambda x: np.linspace(0, 1, x)

f = interp2d(xrange(W), xrange(H), arr, kind="linear")
new_arr = f(xrange(new_W), xrange(new_H))

当然,如果您的图像是RGB,则必须对每个通道执行插值。

如果您想了解更多信息,建议您观看“ 调整图像大小-Computerphile”

Are there any libraries to do this in numpy/SciPy

Sure. You can do this without OpenCV, scikit-image or PIL.

Image resizing is basically mapping the coordinates of each pixel from the original image to its resized position.

Since the coordinates of an image must be integers (think of it as a matrix), if the mapped coordinate has decimal values, you should interpolate the pixel value to approximate it to the integer position (e.g. getting the nearest pixel to that position is known as Nearest neighbor interpolation).

All you need is a function that does this interpolation for you. SciPy has interpolate.interp2d.

You can use it to resize an image in numpy array, say arr, as follows:

W, H = arr.shape[:2]
new_W, new_H = (600,300)
xrange = lambda x: np.linspace(0, 1, x)

f = interp2d(xrange(W), xrange(H), arr, kind="linear")
new_arr = f(xrange(new_W), xrange(new_H))

Of course, if your image is RGB, you have to perform the interpolation for each channel.

If you would like to understand more, I suggest watching Resizing Images – Computerphile.


回答 6

import cv2
import numpy as np

image_read = cv2.imread('filename.jpg',0) 
original_image = np.asarray(image_read)
width , height = 452,452
resize_image = np.zeros(shape=(width,height))

for W in range(width):
    for H in range(height):
        new_width = int( W * original_image.shape[0] / width )
        new_height = int( H * original_image.shape[1] / height )
        resize_image[W][H] = original_image[new_width][new_height]

print("Resized image size : " , resize_image.shape)

cv2.imshow(resize_image)
cv2.waitKey(0)
import cv2
import numpy as np

image_read = cv2.imread('filename.jpg',0) 
original_image = np.asarray(image_read)
width , height = 452,452
resize_image = np.zeros(shape=(width,height))

for W in range(width):
    for H in range(height):
        new_width = int( W * original_image.shape[0] / width )
        new_height = int( H * original_image.shape[1] / height )
        resize_image[W][H] = original_image[new_width][new_height]

print("Resized image size : " , resize_image.shape)

cv2.imshow(resize_image)
cv2.waitKey(0)